package com.codi.bus.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.codi.base.cache.CacheUtil;
import com.codi.base.util.*;
import com.codi.bus.constant.*;
import com.codi.bus.core.dao.*;
import com.codi.bus.core.domain.*;
import com.codi.bus.core.fund.dao.FundPortfolioTransferDao;
import com.codi.bus.core.fund.domain.FundPortfolioTransfer;
import com.codi.bus.core.fund.enums.FundPortfolioTransferStatusEnum;
import com.codi.bus.core.job.FundBasicJobService;
import com.codi.bus.core.resultModel.*;
import com.codi.bus.core.service.*;
import com.codi.bus.core.service.model.*;
import com.codi.bus.core.service.model.ApplyModel.FundApplyType;
import com.codi.bus.core.service.util.*;
import com.codi.bus.exception.BaseException;
import com.codi.bus.exception.HundsunAPIException;
import com.codi.bus.exception.SystemDBException;
import com.codi.bus.util.FundOperation;
import com.codi.fcloud.ufx.constant.HSOutputParameterNames;
import com.codi.fcloud.ufx.constant.UrlConstants;
import com.codi.fcloud.ufx.hssale.query.DiscountSettingQuery;
import com.codi.fcloud.ufx.hssale.query.FixQuery;
import com.codi.fcloud.ufx.hssale.query.RateQuery;
import com.codi.fundData.dao.FundDetailDao;
import com.codi.fundData.domain.FundDetail;
import com.codi.fundData.domain.FundMMYieldPerformance;
import com.codi.fundData.domain.SecurityProduct;
import com.codi.fundData.service.FundMMYieldPerformanceService;
import com.codi.message.constant.ErrorConstant;
import com.codi.message.util.MessageUtil;
import com.codi.trade.dto.*;
import com.codi.trade.service.*;
import com.codi.trade.service.portfolio.*;
import com.github.pagehelper.PageHelper;
import com.google.common.base.Function;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/*
 * 查询类不用记日志
 */
@Service("queryService")
public class QueryServiceImpl implements QueryService {

    private static final Logger logger = LoggerFactory.getLogger(QueryServiceImpl.class);

    @Resource(name = "userDao")
    private UserDao userDao;

    @Resource(name = "userSessionDao")
    private UserSessionDao userSessionDao;

    @Resource(name = "shareQueryService")
    private ShareQueryService shareQueryService;

    @Resource(name = "transactionDao")
    private TransactionDao transactionDao;

    @Resource(name = "userBankDao")
    private UserBankDao userBankDao;

    @Resource(name = "fundQueryService")
    private FundQueryService fundQueryService;

    @Resource(name = "tradeApplyQueryService")
    private TradeApplyQueryService tradeApplyQueryService;

    @Resource(name = "fundDetailService")
    private FundDetailService fundDetailService;

    @Resource(name = "shareTypeQueryService")
    private ShareTypeQueryService shareTypeQueryService;

    @Resource(name = "accountBankQueryService")
    private AccountBankQueryService accountBankQueryService;

    @Resource(name = "rateQuery")
    private RateQuery rateQuery;

    @Autowired
    private DiscountSettingQuery discountSettingQuery;

    @Resource(name = "tradeConfirmQueryService")
    private TradeConfirmQueryService tradeConfirmQueryService;

    @Resource(name = "tradeLimitQueryService")
    private TradeLimitQueryService tradeLimitQueryService;

    @Resource(name = "newsQueryService")
    private NewsQueryService newsQueryService;

    @Resource(name = "accountQueryService")
    private AccountQueryService accountQueryService;

    @Resource(name = "fixQuery")
    private FixQuery fixQuery;

    @Resource(name = "lookupDao")
    private LookupDao lookupDao;

    @Resource(name = "tradeOrderDao")
    private TradeOrderDao tradeOrderDao;

    @Autowired
    private TradeOrdersDao tradeOrdersDao;

    @Resource(name = "nextDayQueryService")
    private NextDayQueryService nextDayQueryService;

    @Resource(name = "workingDateService")
    private WorkingDateService workingDateService;

    @Resource(name = "dividendsQueryService")
    private DividendsQueryService dividendsQueryService;

    @Autowired
    private WarningContentQueryService warningContentQueryService;

    @Autowired
    private AccountApplyQryService accountApplyQryService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private FundBasicJobService fundBasicJobService;

    @Autowired
    private HsSharePortfolioCodeQryService hsSharePortfolioCodeQryService;

    @Autowired
    private HsPortfolioQryService portfolioQryService;

    @Autowired
    private HsShareDetailQryService hsShareDetailQryService;

    @Autowired
    private PortfolioService portfolioService;

    @Autowired
    private HsPortfolioSummaryQryService portfolioSummaryQryService;

    @Autowired
    private HsCombSharesQryService hsCombSharesQryService;

    @Autowired
    private HsCombIncomeQryService hsCombIncomeQryService;

    @Autowired
    private IncomeQueryService incomeQueryService;

    @Autowired
    private BankNameDao bankNameDao;

    @Autowired
    private CityNameDao cityNameDao;

    @Autowired
    private BankService bankService;

    @Autowired
    private FundWarehouseTransferDao fundWarehouseTransferDao;

    @Autowired
    private FundCombinationService fundCombinationService;

    @Autowired
    private FundPortfolioTransferDao fundPortfolioTransferDao;

    @Autowired
    private FundMMYieldPerformanceService fundMMYieldPerformanceService;

    @Autowired
    private FundDetailDao fundDetailDao;

    @Autowired
    private FundCombinationMappingDao fundCombinationMappingDao;

    @Autowired
    private LookupService lookupService;

    @Autowired
    private ProfitDao profitDao;

    @Autowired
    private AssetsQueryService assetsQueryService;

    /**
     * 交易确认查询
     *
     * @param userId
     * @param requestNum
     * @param queryFlag
     * @param beginNum
     * @param sortDirection
     * @param fundCode
     * @param businCode
     * @param beginDate
     * @param endDate
     * @param confirmFlag
     * @return
     */
    @Override
    public CollectionResult<ConfirmModel> queryTradeConfirms(Long userId, Integer requestNum, Integer queryFlag, Integer beginNum,
                                                             String sortDirection, String fundCode, String businCode, Date beginDate, Date endDate, String confirmFlag) {

        CollectionResult<ConfirmModel> result = new CollectionResult<>();
        try {

            // 获取用户
            User user = userDao.getEntity(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            Map<String, Object> map = tradeConfirmQueryService.query(requestNum, queryFlag, beginNum, sortDirection, "", user.getExternalClientId(), DateUtils.formatDate4(beginDate), DateUtils.formatDate4(endDate), "", confirmFlag, "", fundCode, businCode, "", "", "", "", "", "", "");

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(list);

            result.setObjs(confirms);

            return result;

        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryTradeConfirms - Exception:", exception);
            throw new SystemDBException("queryTradeConfirms Failure!", exception);
        }
    }

    /**
     * Shangdu Lin: 查询某一用户下的某一时间段的所有交易记录, 此方法被用于计算当前交易日T0基金快速赎回限制
     *
     * @param userId
     * @param beginDate
     * @param endDate
     * @param businCode
     * @param onlyT0
     * @return
     * @throws BaseException
     */
    @Override
    public ApplyListResult queryApplys(Long userId, Date beginDate, Date endDate, String businCode, String confirmFlag,
                                       boolean onlyT0) throws BaseException {

        ApplyListResult result = new ApplyListResult();

        try {

            // 获取用户
            User user = userDao.getEntity(userId);

            String beginDateString = DateUtils.formatDate4(beginDate);
            String endDateString = DateUtils.formatDate4(endDate);
            // 查出这个用户下当前时间段所有的交易记录，包括已确认和未确认的，传ClientId
            Map<String, Object> map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), beginDateString, endDateString, "", confirmFlag, "", "", businCode, "",
                "", "", "", "");

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // 交易业务名称
            List<Lookup> tradeLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
            // 银行名称
            List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            // populate
            List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, result, tradeLookups, bankLookups);
            // 只计算T0基金
            if (onlyT0) {
                List<Lookup> T0Funds = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
                Iterator<ApplyModel> iterator = applys.iterator();
                while (iterator.hasNext()) {
                    ApplyModel model = iterator.next();
                    if (!ModelPopulateUtils.isT0Fund(T0Funds, model.getFundCode())) {
                        iterator.remove();
                    }
                }
            }

            result.setSuccess(true);
            result.setApplys(applys);

            return result;

        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryApplys - Exception:", exception);
            throw new SystemDBException("queryApplys Failure!", exception);
        }
    }

    /**
     * 我的资产 机构 页面数据查询 -zhenhao wang
     *
     * @param userId
     * @return 出参：总资产，当日收益，零钱包：{总资产，当日收益，净值日期，是否有在途}，基金：{总资产，当日收益，净值日期，是否有在途，
     * 基金数} { "success":true, "funds":[ { "assetType":1, "fundsCount":1,
     * "inTransit":false, "netValueDateString":"11-28",
     * "totalTodayIncomeString":"4.31",
     * "totalWorthValueString":"70,000.98" }, { "assetType":2,
     * "fundsCount":1, "inTransit":true, "netValueDateString":"11-28",
     * "totalTodayIncomeString":"87.82",
     * "totalWorthValueString":"505,100.37" } ],
     * "totalTodayIncomeString":"92.13",
     * "totalWorthValueString":"575,101.35" }
     * @throws BaseException
     */
    @Override
    public AssetsResult queryOrganAssets(Long userId) throws BaseException {
        try {
            /**
             * Step:0 验证数据
             *
             */
            //结果
            AssetsResult result = new AssetsResult();
            User user = userDao.getEntity(userId);

            //判断是否开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            /**
             * Step:0-1 查询当前工作日
             */
            Date currentWorkingDay = workingDateService.getWorkingDay(new Date());


        } catch (BaseException e) {
            throw e;
        } catch (Exception ex) {
            ExceptionUtil.logError(logger, "queryAssets - Exception:", ex);
            throw new SystemDBException("queryAssets Failure!", ex);
        }

        return null;
    }

    /**
     * 我的资产页面数据查询 -Shangdu Lin
     *
     * @param userId
     * @return 出参：总资产，当日收益，零钱包：{总资产，当日收益，净值日期，是否有在途}，基金：{总资产，当日收益，净值日期，是否有在途，
     * 基金数} { "success":true, "funds":[ { "assetType":1, "fundsCount":1,
     * "inTransit":false, "netValueDateString":"11-28",
     * "totalTodayIncomeString":"4.31",
     * "totalWorthValueString":"70,000.98" }, { "assetType":2,
     * "fundsCount":1, "inTransit":true, "netValueDateString":"11-28",
     * "totalTodayIncomeString":"87.82",
     * "totalWorthValueString":"505,100.37" } ],
     * "totalTodayIncomeString":"92.13",
     * "totalWorthValueString":"575,101.35" }
     * @throws BaseException
     */
    @Override
    public AssetsResult queryAssets(Long userId) throws BaseException {
        try {

            /**
             * Step 0: 验证数据
             */
            // 结果
            AssetsResult result = new AssetsResult();
            // 获取用户
            User user = userDao.getEntity(userId);
            // 获取 交易账号
            List<UserBank> userBankList = userBankDao.getListByUserId(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            // 获取T+0基金
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
            /**
             * Step 0-1:查询当前工作日
             */
            // 查询当前工作日
            Date currentWorkingDate = getCurrencyDate();
            // 根据恒生银行编号获取银行名称
            List<Lookup> banklookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            /**
             * Step 1: 资产查询
             */
            // 查询该用户下的所有份额
            HsShareQueryDto hsShareQueryDto = HsShareQueryDto.builder()
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .reqry_recordsum_flag("1")
                .qry_beginrownum(1)
                .sort_direction("1")
                .client_id(user.getExternalClientId())
                .build();
            Map<String, Object> map = shareQueryService.query(hsShareQueryDto);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            ShareListResult shareListResult = new ShareListResult();
            List<ShareModel> shares = ModelPopulateUtils.populateShare(list, null, shareListResult);

            //份额明细
            ShareDetailQryDto shareDetailQryDto = ShareDetailQryDto.builder()
                .qry_beginrownum("1")
                .begin_date(DateUtils.formatDate4(new Date()))
                .client_id(user.getExternalClientId())
                .end_date(DateUtils.formatDate4(new Date()))
                .reqry_recordsum_flag("1")
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .sort_direction("1")
                .build();
            map = hsShareDetailQryService.query(shareDetailQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            //普通基金
            List<ShareDetailQryModel> shareDetailQryFundModels = new ArrayList<>();

            //组合
            List<ShareDetailQryModel> shareDetailQryPortfolioModels = null;
            List<String> tradeAccounts = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        //累加普通基金  份额明细
                        shareDetailQryFundModels = ModelPopulateUtils.populateShareDetailQryFund(list, userBank);
                        //将交易账号 合并
                        tradeAccounts.add(userBank.getExternalTradeAccount());
                    }
                }
            }
            shareDetailQryPortfolioModels = ModelPopulateUtils.populateShareDetailQryPorefolio(list, tradeAccounts);

            /**
             * 普通基金份额查询  非组合部分
             */
//            Map<String, ShareModel> shareMap = new HashMap<>();
            List<ShareModel> normalShares = null;
            List<Map<String, Object>> listAll = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        // 查询该用户下的所有非组合基金份额
                        map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, userBank.getExternalTradeAccount(), "1", "", "", "",
                            user.getExternalClientId(), "", "", null);
                        logger.warn("map：{}", map.get(GlobalConstant.KEY_LIST_MAP));

                        list = new ArrayList<>();
                        HundsunUtils.ConvertToList(map, list, result);
                        if (!result.getSuccess()) {
                            return result;
                        }
                        listAll.addAll(list);



                    }
                }
            }
            normalShares = ModelPopulateUtils.populateNormalShare(listAll, banklookups);
            //恒生 按交易账号查询 分红
            List<DividendModel> dividends = getDividend(user,userBankList);
            //恒生 按交易账号查询 收益
            //List<IncomeModel> incomeModels = getIncome(user,userBankList);

            /**
             *  组合份额查询
             */
            //CombSharesQryDto combSharesQryDto = hsCombSharesQryService.getDto("", "", "", "", GlobalConstant.MAX_REQUEST_NUM.toString(), "1", "", user.getExternalClientId(), "1", "", "", "", "");
            CombSharesQryDto combSharesQryDto = CombSharesQryDto.builder()
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .reqry_recordsum_flag("1")
                .client_id(user.getExternalClientId())
                .qry_beginrownum("1")
                .build();
            map = hsCombSharesQryService.query(combSharesQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            List<CombSharesQryModel> combSharesQryModels = ModelPopulateUtils.populateCombSharesQry(list);
            //将同一组合的基金放在一起  存入一个map
            Map<String, List<CombSharesQryModel>> combMap = new HashMap<>();
            List<String> combCode = new ArrayList<>();
            Map<String, List<CombSharesQryModel>> combTradeMap = new HashMap<>();
            //按交易账号区分
            portfolioService.getCombTradeMap(combSharesQryModels, combTradeMap);
            portfolioService.getCombMap(combSharesQryModels, combMap, combCode);

            //恒生 查组合
            List<PortfolioQryModel> portfolioQryModels = getPortfolioQryModel(user);
            Map<String, List<PortfolioQryModel>> portfolioMap = new HashMap<>();
            List<String> portfolioCode = new ArrayList<>();
            //PortfolioQryModelByCode portfolioQryModelByCode = new PortfolioQryModelByCode();
            portfolioService.getPortfolioMap(portfolioQryModels, portfolioMap, portfolioCode);
            //Map<String, List<PortfolioQryModel>> portfolioMap = new HashMap<>();

            // 收益查询  组合    按交易账号查
            //Map<String, List<IncomeModel>> mapIncome = getCombIncome(combTradeMap, user);

            /**
             * Step 2: 交易记录查询
             */
            // 查出这个用户下所有的未确认的交易记录，传ClientId
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", GlobalConstant.TACONFIRM_FLAG_UNCONFIRM, "", "", "", "", "", "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // populate applys
            ApplyListResult applyListResult = new ApplyListResult();
            List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, applyListResult, null, null);
            List<String> applyPortfolioCode = new ArrayList<>();
            for (ApplyModel applyModel : applys) {
                if (!Strings.isNullOrEmpty(applyModel.getPortfolioCode()) && !applyPortfolioCode.contains(applyModel.getPortfolioCode())) {
                    applyPortfolioCode.add(applyModel.getPortfolioCode());
                }
            }

            //交易申请查询  用于计算瑞富宝累计收益
            list = new ArrayList<>();
             map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "");


            // 查询结果
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // 交易业务名称
            List<Lookup> tradeLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
            // 银行名称
            List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            // populate
            List<ApplyModel> applyModels = ModelPopulateUtils.populateApply2(list, applyListResult, tradeLookups, bankLookups, user.getIsOrg());


            //交易确认  用于计算累计收益
            map = tradeConfirmQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(list);


            /**
             * Step 3: 从聚源获取基金信息
             */

            // 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (ShareModel shareModel : shares) {
                if (shareModel.getFundCode().equals("161728")){
                    continue;
                }
                if (!fundCodeList.contains(shareModel.getFundCode())){
                    fundCodeList.add(shareModel.getFundCode());
                }
            }

            List<FundNetValueResult> fundNetValueList = null;
            boolean gliDataException = false;
            try {
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, DateUtils.addDay(new Date(), -1));
            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryAssets - fundDetailService - Exception:", ex);
                gliDataException = true;
            }

            // 如果没有找到聚源的，返回错误
            if (gliDataException || fundNetValueList == null) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                return result;
            }

            // 获取上一工作日净值
            Map<String, BigDecimal> lastNetValues = new HashedMap();
            for (FundNetValueResult netValueResult :
                fundNetValueList) {
                if (netValueResult != null) {
                    if (!lastNetValues.containsKey(netValueResult.getFundCode())) {
                        BigDecimal lastUnitValue = getLastUnitValue(netValueResult.getFundCode(), netValueResult.getTradingDay());
                        lastNetValues.put(netValueResult.getFundCode(), lastUnitValue);
                    }
                }
            }


            /**
             * Step 4: 计算出前后15天未确认的快速赎回的份额（如果要精确计算的话，只要算当前工作日以及上一工作日的未T+
             * 1确认的实时成功的快速赎回的份额）
             */
//            Map<String, BigDecimal> fastRedeemShares = getFastRedeemSharesWithInterval(userId, new Date(),
//                GlobalConstant.FAST_REDEEM_INTERVAL_DAYS_AFTER_BEFORE);

            Date beginDate = DateUtils.addDay(new Date(), GlobalConstant.FAST_REDEEM_INTERVAL_DAYS_AFTER_BEFORE * (-1));
            Date endDate = DateUtils.addDay(new Date(), GlobalConstant.FAST_REDEEM_INTERVAL_DAYS_AFTER_BEFORE);

            applyListResult = queryApplys(userId, beginDate, endDate,
                GlobalConstant.FUND_BUSIN_CODE_T0_FAST_APPLY_SELL, GlobalConstant.TACONFIRM_FLAG_REAL_SUCCESS,
                true);
            if (!applyListResult.getSuccess()) {
                throw new BaseException(applyListResult.getErrorCode(), applyListResult.getErrorMessage());
            }

            //近期快速赎回的份额
            Map<String, BigDecimal> fastRedeemShares = CalculateAssetsUtils.getFastSellShares(applyListResult.getApplys());

            //当日快速赎回份额
            Map<String, BigDecimal> todayFastRedeemShares = CalculateAssetsUtils.getTodayFastSellShares(applyListResult.getApplys());

            /**
             * Step 5: 计算结果值
             */
            //计算组合份额，资产 日收益
            List<PortfolioModel> portfolioModels = new ArrayList<>();

            //按交易账号 区分开的分红列表
            Map<String, List<DividendModel>> dividendMap = new HashMap<>();
            for (String key : combTradeMap.keySet()) {
                getDividends(key, user, dividendMap);
            }

            //组合收益
//            CombIncomeQryDto combIncomeQryDto = CombIncomeQryDto.builder()
//                .request_num(GlobalConstant.MAX_REQUEST_NUM)
//                .reqry_recordsum_flag("1")
//                .sort_direction("1")
//                .client_id(user.getExternalClientId())
//                .qry_beginrownum("1")
//                .build();
//            map = hsCombIncomeQryService.query(combIncomeQryDto);
//            list = new ArrayList<>();
//            HundsunUtils.ConvertToList(map, list, result);
//            if (!result.getSuccess()) {
//                return result;
//            }
//            Map<String, CombIncomeQryModel> modelMap = ModelPopulateUtils.populateCombIncomeQry(list);

            for (String code : combCode) {
                if (combMap.get(code) == null || combMap.get(code).isEmpty()) {
                    continue;
                }
                PortfolioModel portfolioModel = CalculateAssetsUtils.extractPortfolio(code, lookups, combMap.get(code), fundNetValueList, lastNetValues, portfolioMap, shareDetailQryPortfolioModels, dividendMap, confirms);
                if (portfolioModel.getEnableShare() == null && portfolioModel.getCurrentShare() == null){
                    continue;
                }
                portfolioModels.add(portfolioModel);
            }
            //将组合存入资产页面组合模型中  并计算出组合的总资产和当日总收益
            AssetPortfolioModel assetPortfolioModel = new AssetPortfolioModel();
            assetPortfolioModel.setPortfolios(portfolioModels);
            BigDecimal portfolioWorthValue = new BigDecimal(0);
            BigDecimal portfolioTodayIncome = new BigDecimal(0);
            BigDecimal portfolioAccumIncome = new BigDecimal(0);
            List<String> portfolioTradeAccounts = new ArrayList<>();
            int count = 0;
            AssetPortfolioModel assetPortfolioForInTransit = CalculateAssetsUtils.extractInTransit(lookups, applys, applyPortfolioCode);

            for (PortfolioModel portfolioModel : portfolioModels) {
                portfolioTradeAccounts.add(portfolioModel.getTradeAcco());
                portfolioTodayIncome = MoneyUtils.add(portfolioTodayIncome, portfolioModel.getPortfolioTodayIncome());
                portfolioWorthValue = MoneyUtils.add(portfolioWorthValue, portfolioModel.getPortfolioWorthValue());
                portfolioAccumIncome = MoneyUtils.add(portfolioAccumIncome, portfolioModel.getPortfolioAccumIncome());
                count++;
            }

            //累计收益加上全赎    和分红
            //需要将全赎的交易账号提取出来  用于获取这个交易账号的 全部分红
            logger.debug("交易账号数量：{}",portfolioTradeAccounts.size());
            for (String portfolioTradeAccount : portfolioTradeAccounts){
                logger.debug("交易账号：{}",portfolioTradeAccount);
            }
            portfolioAccumIncome = CalculateAssetsUtils.getPortfolioAccumIncome(confirms,portfolioTradeAccounts, portfolioAccumIncome,dividendMap);

            portfolioWorthValue = MoneyUtils.add(portfolioWorthValue, assetPortfolioForInTransit.getInTransitAmount());
            assetPortfolioModel.setPortfolioTodayIncome(portfolioTodayIncome);
            assetPortfolioModel.setPortfolioWorthValue(portfolioWorthValue);
            assetPortfolioModel.setPortfolioAccumIncome(portfolioAccumIncome);
            assetPortfolioModel.setInTransitAmount(assetPortfolioForInTransit.getInTransitAmount());
            assetPortfolioModel.setInTransit(assetPortfolioForInTransit.isInTransit());
            assetPortfolioModel.setInTransitNum(assetPortfolioForInTransit.getInTransitNum());
            assetPortfolioModel.setPortfolioNum(count);
            // 计算份额，净值日期， 日收益率，万份收益
            List<AssetFundModel> funds = CalculateAssetsUtils.extractFunds(shares, lookups, fundNetValueList);
            // 计算总和值，是否有在途
            // 汇总列表
            List<AssetFundModel> sumOfFunds = new ArrayList<>();
            // 零钱包 - 计算零钱包总资产和零钱包总收益
            // 零钱包资产要减去当前工作日已快速赎回的部分，因为快速赎回的部分当前工作日已经没有日收益
            AssetFundModel wallet = CalculateAssetsUtils.sumOfAsset(funds, applys, lookups, true, fastRedeemShares,
                null, todayFastRedeemShares, shareDetailQryFundModels, dividends,confirms, applyModels);
            sumOfFunds.add(wallet);



            //getAccumIncome(wallet,  mapDividend, incomeModelsWallet);


            /**
             * 非组合基金的计算
             */
            // 排除组合后的基金  应该还是包括T0基金的  这里依旧沿用之前的方法  因为获取份额的恒生接口是同一个 所以没有太大的区别  排除掉组合份额后 就可以使用原方法
            //logger.warn("normalSize:{}", normalShares.size());

            List<AssetFundModel> normalFunds = CalculateAssetsUtils.extractFunds(normalShares, lookups, fundNetValueList);
            AssetFundModel normalFund = CalculateAssetsUtils.sumOfAsset(normalFunds, applys, lookups, false, fastRedeemShares,
                lastNetValues, todayFastRedeemShares, shareDetailQryFundModels, dividends,confirms,applyModels);

            // 总资产
            BigDecimal totalWorthValue = MoneyUtils.add(MoneyUtils.add(wallet.getTotalWorthValue(), normalFund.getTotalWorthValue()), assetPortfolioModel.getPortfolioWorthValue());
            // 总收益
            BigDecimal totalTodayIncome = MoneyUtils.add(MoneyUtils.add(wallet.getTotalTodayIncome(), normalFund.getTotalTodayIncome()), assetPortfolioModel.getPortfolioTodayIncome());
            // 总在途金额
            BigDecimal totalInTransitAmount = MoneyUtils.add(MoneyUtils.add(wallet.getInTransitAmount(), normalFund.getInTransitAmount()), assetPortfolioModel.getInTransitAmount());
            //总累计收益
            BigDecimal totalAccumIncome = MoneyUtils.add(MoneyUtils.add(wallet.getTotalAccumIncome(), normalFund.getTotalAccumIncome()), assetPortfolioModel.getPortfolioAccumIncome());
            //总在途笔数
            Integer totalInTransitNum = wallet.getInTransitNum() + normalFund.getInTransitNum() + assetPortfolioModel.getInTransitNum();

            // 返回结果
            result.setSuccess(true);
            // 零钱包
            result.setWallet(wallet);
            // 基金  非组合
            result.setFund(normalFund);
            // 总资产
            result.setTotalWorthValue(totalWorthValue);
            // 当日收益
            result.setTotalTodayIncome(totalTodayIncome);
            //累计收益
            result.setTotalAccumIncome(totalAccumIncome);
            // 总在途金额
            result.setTotalInTransitAmount(totalInTransitAmount);
            //总在途笔数
            result.setTotalInTransitNum(totalInTransitNum);
            //组合
            result.setPortfolios(assetPortfolioModel);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryAssets - Exception:", exception);
            throw new SystemDBException("queryAssets Failure!", exception);
        }
    }


    /**
     * 普通基金资产查询
     * @param userId
     * @return
     * @throws BaseException
     */
    @Override
    public AssetsResult queryFundAssets(Long userId) throws BaseException {
        try {

            /**
             * Step 0: 验证数据
             */
            // 结果
            AssetsResult result = new AssetsResult();
            // 获取用户
            User user = userDao.getEntity(userId);
            // 获取 交易账号
            List<UserBank> userBankList = userBankDao.getListByUserId(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            // 获取T+0基金
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
            /**
             * Step 0-1:查询当前工作日
             */
            // 根据恒生银行编号获取银行名称
            List<Lookup> banklookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            /**
             * Step 1: 资产查询
             */
            // 查询该用户下的所有份额
            HsShareQueryDto hsShareQueryDto = HsShareQueryDto.builder()
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .reqry_recordsum_flag("1")
                .qry_beginrownum(1)
                .sort_direction("1")
                .client_id(user.getExternalClientId())
                .build();
            Map<String, Object> map = shareQueryService.query(hsShareQueryDto);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            ShareListResult shareListResult = new ShareListResult();
            List<ShareModel> shares = ModelPopulateUtils.populateShare(list, null, shareListResult);

            //份额明细
            ShareDetailQryDto shareDetailQryDto = ShareDetailQryDto.builder()
                .qry_beginrownum("1")
                .begin_date(DateUtils.formatDate4(new Date()))
                .client_id(user.getExternalClientId())
                .end_date(DateUtils.formatDate4(new Date()))
                .reqry_recordsum_flag("1")
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .sort_direction("1")
                .build();
            map = hsShareDetailQryService.query(shareDetailQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            //普通基金
            List<ShareDetailQryModel> shareDetailQryFundModels = new ArrayList<>();

            //组合
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        //累加普通基金  份额明细
                        shareDetailQryFundModels = ModelPopulateUtils.populateShareDetailQryFund(list, userBank);

                    }
                }
            }

            /**
             * 普通基金份额查询  非组合部分
             */
//            Map<String, ShareModel> shareMap = new HashMap<>();
            List<ShareModel> normalShares = null;
            List<Map<String, Object>> listAll = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        // 查询该用户下的所有非组合基金份额
                        map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, userBank.getExternalTradeAccount(), "1", "", "", "",
                            user.getExternalClientId(), "", "", null);
                        logger.warn("map：{}", map.get(GlobalConstant.KEY_LIST_MAP));

                        list = new ArrayList<>();
                        HundsunUtils.ConvertToList(map, list, result);
                        if (!result.getSuccess()) {
                            return result;
                        }
                        listAll.addAll(list);

                    }
                }
            }
            normalShares = ModelPopulateUtils.populateNormalShare(listAll, banklookups);
            //恒生 按交易账号查询 分红
            List<DividendModel> dividends = getDividend(user,userBankList);
            //恒生 按交易账号查询 收益
            //List<IncomeModel> incomeModels = getIncome(user,userBankList);



            /**
             * Step 2: 交易记录查询
             */
            // 查出这个用户下所有的未确认的交易记录，传ClientId
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", GlobalConstant.TACONFIRM_FLAG_UNCONFIRM, "", "", "", "", "", "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // populate applys
            ApplyListResult applyListResult = new ApplyListResult();
            List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, applyListResult, null, null);
            List<String> applyPortfolioCode = new ArrayList<>();
            for (ApplyModel applyModel : applys) {
                if (!Strings.isNullOrEmpty(applyModel.getPortfolioCode()) && !applyPortfolioCode.contains(applyModel.getPortfolioCode())) {
                    applyPortfolioCode.add(applyModel.getPortfolioCode());
                }
            }

            //交易申请查询  用于计算瑞富宝累计收益
            list = new ArrayList<>();
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "");


            // 查询结果
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // 交易业务名称
            List<Lookup> tradeLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
            // 银行名称
            List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            // populate
            List<ApplyModel> applyModels = ModelPopulateUtils.populateApply2(list, applyListResult, tradeLookups, bankLookups, user.getIsOrg());


            //交易确认  用于计算累计收益
            map = tradeConfirmQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(list);


            /**
             * Step 3: 从聚源获取基金信息
             */

            // 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (ShareModel shareModel : shares) {
                if (!fundCodeList.contains(shareModel.getFundCode())){
                    fundCodeList.add(shareModel.getFundCode());
                }
            }

            List<FundNetValueResult> fundNetValueList = null;
            boolean gliDataException = false;
            try {
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, DateUtils.addDay(new Date(), -1));
            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryAssets - fundDetailService - Exception:", ex);
                gliDataException = true;
            }

            // 如果没有找到聚源的，返回错误
            if (gliDataException || fundNetValueList == null) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                return result;
            }

            // 获取上一工作日净值
            Map<String, BigDecimal> lastNetValues = new HashedMap();
            for (FundNetValueResult netValueResult :
                fundNetValueList) {
                if (netValueResult != null) {
                    if (!lastNetValues.containsKey(netValueResult.getFundCode())) {
                        BigDecimal lastUnitValue = getLastUnitValue(netValueResult.getFundCode(), netValueResult.getTradingDay());
                        lastNetValues.put(netValueResult.getFundCode(), lastUnitValue);
                    }
                }
            }




            /**
             * Step 5: 计算结果值
             */
            // 计算份额，净值日期， 日收益率，万份收益
            List<AssetFundModel> funds = CalculateAssetsUtils.extractFunds(shares, lookups, fundNetValueList);
            // 计算总和值，是否有在途
            // 汇总列表
            List<AssetFundModel> sumOfFunds = new ArrayList<>();


            /**
             * 非组合基金的计算
             */
            // 排除组合后的基金  应该还是包括T0基金的  这里依旧沿用之前的方法  因为获取份额的恒生接口是同一个 所以没有太大的区别  排除掉组合份额后 就可以使用原方法
            //logger.warn("normalSize:{}", normalShares.size());

            List<AssetFundModel> normalFunds = CalculateAssetsUtils.extractFunds(normalShares, lookups, fundNetValueList);
            AssetFundModel normalFund = CalculateAssetsUtils.sumOfAsset(normalFunds, applys, lookups, false, null,
                lastNetValues, null, shareDetailQryFundModels, dividends,confirms,applyModels);

            // 返回结果
            result.setSuccess(true);

            // 基金  非组合
            result.setFund(normalFund);


            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryAssets - Exception:", exception);
            throw new SystemDBException("queryAssets Failure!", exception);
        }
    }

    /**
     * 瑞富宝资金查询
     *
     * @param userId
     * @return
     * @throws BaseException
     */
    @Override
    public AssetsResult queryWalletAssets(Long userId) throws BaseException {
        try {
            AssetsResult result = new AssetsResult();
            /*
             * Step 0: 验证数据
             */
            // 获取用户
            User user = userDao.getEntity(userId);
            // 获取交易账号
            List<UserBank> userBankList = userBankDao.getListByUserId(userId);
            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }
            // 获取T+0基金
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
            /*
             * Step 1: 资产查询
             */
            // 查询该用户下的所有份额
            HsShareQueryDto hsShareQueryDto = HsShareQueryDto.builder()
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .reqry_recordsum_flag("1")
                .qry_beginrownum(1)
                .sort_direction("1")
                .client_id(user.getExternalClientId())
                .build();
            Map<String, Object> map = shareQueryService.query(hsShareQueryDto);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            ShareListResult shareListResult = new ShareListResult();
            List<ShareModel> shares = ModelPopulateUtils.populateShare(list, null, shareListResult);

            //份额明细
            ShareDetailQryDto shareDetailQryDto = ShareDetailQryDto.builder()
                .qry_beginrownum("1")
                .begin_date(DateUtils.formatDate4(new Date()))
                .client_id(user.getExternalClientId())
                .end_date(DateUtils.formatDate4(new Date()))
                .reqry_recordsum_flag("1")
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .sort_direction("1")
                .build();
            map = hsShareDetailQryService.query(shareDetailQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }


            //普通基金
            List<ShareDetailQryModel> shareDetailQryFundModels = new ArrayList<>();

            /*
             * 普通基金份额查询  非组合部分
             */

            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        //累加普通基金  份额明细
                        shareDetailQryFundModels = ModelPopulateUtils.populateShareDetailQryFund(list, userBank);
                    }
                }
            }
            // populate
            List<DividendModel> dividends = getDividend(user,userBankList);


            /*
             * Step 2: 交易记录查询
             */
            // 查出这个用户下所有的未确认的交易记录，传ClientId
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", GlobalConstant.TACONFIRM_FLAG_UNCONFIRM, "", "", "", "", "", "", "", "");
            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // populate applys
            ApplyListResult applyListResult = new ApplyListResult();
            List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, applyListResult, null, null);
            List<String> applyPortfolioCode = new ArrayList<>();
            for (ApplyModel applyModel : applys) {
                if (!Strings.isNullOrEmpty(applyModel.getPortfolioCode()) && !applyPortfolioCode.contains(applyModel.getPortfolioCode())) {
                    applyPortfolioCode.add(applyModel.getPortfolioCode());
                }
            }

            //交易申请查询  用于计算瑞富宝累计收益
            list = new ArrayList<>();
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "");


            // 查询结果
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // 交易业务名称
            List<Lookup> tradeLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
            // 银行名称
            List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            // populate
            List<ApplyModel> applyModels = ModelPopulateUtils.populateApply2(list, applyListResult, tradeLookups, bankLookups, user.getIsOrg());


            //交易确认
            map = tradeConfirmQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "", "", "");
            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(list);



            /*
             * Step 3: 获取T+0基金以及从聚源获取基金信息
             */

            // 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<>();
            for (ShareModel shareModel : shares) {
                //先过滤掉  还没有基金信息
                if (shareModel.getFundCode().equals("161728")){
                    continue;
                }
                fundCodeList.add(shareModel.getFundCode());
            }

            List<FundNetValueResult> fundNetValueList = null;
            boolean gliDataException = false;
            try {
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, DateUtils.addDay(new Date(), -1));
            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryAssets - fundDetailService - Exception:", ex);
                gliDataException = true;
            }

            // 如果没有找到聚源的，返回错误
            if (gliDataException || fundNetValueList == null) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                return result;
            }

            // 获取上一工作日净值
            Map<String, BigDecimal> lastNetValues = new HashMap<>();
            for (FundNetValueResult netValueResult : fundNetValueList) {
                if (netValueResult != null) {
                    if (!lastNetValues.containsKey(netValueResult.getFundCode())) {
                        logger.debug("基金编码：{}，净值日期：{}");
                        BigDecimal lastUnitValue = getLastUnitValue(netValueResult.getFundCode(), netValueResult.getTradingDay());
                        lastNetValues.put(netValueResult.getFundCode(), lastUnitValue);
                    }
                }
            }
            /*
             * Step 4: 计算出前后15天未确认的快速赎回的份额（如果要精确计算的话，只要算当前工作日以及上一工作日的未T+
             * 1确认的实时成功的快速赎回的份额）
             */
            Date beginDate = DateUtils.addDay(new Date(), GlobalConstant.FAST_REDEEM_INTERVAL_DAYS_AFTER_BEFORE * (-1));
            Date endDate = DateUtils.addDay(new Date(), GlobalConstant.FAST_REDEEM_INTERVAL_DAYS_AFTER_BEFORE);

            applyListResult = queryApplys(userId, beginDate, endDate,
                GlobalConstant.FUND_BUSIN_CODE_T0_FAST_APPLY_SELL, GlobalConstant.TACONFIRM_FLAG_REAL_SUCCESS,
                true);
            if (!applyListResult.getSuccess()) {
                throw new BaseException(applyListResult.getErrorCode(), applyListResult.getErrorMessage());
            }

            //近期快速赎回的份额
            Map<String, BigDecimal> fastRedeemShares = CalculateAssetsUtils.getFastSellShares(applyListResult.getApplys());

            //当日快速赎回份额
            Map<String, BigDecimal> todayFastRedeemShares = CalculateAssetsUtils.getTodayFastSellShares(applyListResult.getApplys());

            /*
             * Step 5: 计算结果值
             */
            // 计算份额，净值日期， 日收益率，万份收益
            List<AssetFundModel> funds = CalculateAssetsUtils.extractFunds(shares, lookups, fundNetValueList);
            // 零钱包 - 计算零钱包总资产和零钱包总收益
            // 零钱包资产要减去当前工作日已快速赎回的部分，因为快速赎回的部分当前工作日已经没有日收益
            AssetFundModel wallet = CalculateAssetsUtils.sumOfAsset(funds, applys, lookups, true, fastRedeemShares,
                null, todayFastRedeemShares, shareDetailQryFundModels, dividends, confirms, applyModels);

            result.setWallet(wallet);
            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryAssets - Exception:", exception);
            throw new SystemDBException("queryAssets Failure!", exception);
        }
    }


    /**
     * 根据交易账号查分红明细
     *
     * @param key
     * @return
     */
    private void getDividends(String key, User user, Map<String, List<DividendModel>> dividendMap) {
        String diviKey = CacheUtil.getKey(UrlConstants.DIVI_QRY, user.getExternalClientId(),key);

        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

        String diviQry = valueOperations.get(diviKey);

        List<DividendModel> dividends = new ArrayList<>();
        if (StringUtil.isNotEmpty(diviQry)) {
            try {
                dividends = JSONArray.parseArray(diviQry, DividendModel.class);
            } catch (Exception e) {
                logger.error("fail to parse array", e);
            }
        }else {
            Map<String, Object> map = dividendsQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", key, "", user.getExternalClientId(), "");
            List<Map<String, Object>> list = new ArrayList<>();
            PortfolioModel result = new PortfolioModel();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                throw new BaseException(result.getErrorCode(), result.getErrorMessage());
            }
            // populate
            BaseListResult baseListResult = new BankListResult();
            dividends = ModelPopulateUtils.populateDividend(list, baseListResult);
            valueOperations.set(diviKey, JSONArray.toJSONString(dividends), GlobalConstant.ASSETS_EXPIRED_HOURS_DEFAULT, TimeUnit.MINUTES);

        }

        dividendMap.put(key, dividends);
    }


    @Override
    public AssetsPortfolioResult queryPortfolioAssets(Long userId) throws BaseException {
        try {

            /**
             * Step 0: 验证数据
             */
            // 结果
            AssetsPortfolioResult result = new AssetsPortfolioResult();
            // 获取用户
            User user = userDao.getEntity(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                return result;
            }

            /**
             * Step 1: 资产查询
             */
            // 查询该用户下的所有份额
            Map<String, Object> map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "", "1", "", "", "",
                user.getExternalClientId(), "", "", null);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            ShareListResult shareListResult = new ShareListResult();
            List<ShareModel> shares = ModelPopulateUtils.populateShare(list, null, shareListResult);


            //份额明细
            ShareDetailQryDto shareDetailQryDto = ShareDetailQryDto.builder()
                .qry_beginrownum("1")
                .begin_date(DateUtils.formatDate4(new Date()))
                .client_id(user.getExternalClientId())
                .end_date(DateUtils.formatDate4(new Date()))
                .reqry_recordsum_flag("1")
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .sort_direction("1")
                .build();
            map = hsShareDetailQryService.query(shareDetailQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            List<UserBank> userBankList = userBankDao.getListByUserId(userId);

            //组合
            List<ShareDetailQryModel> shareDetailQryPortfolioModels = new ArrayList<>();
            List<String> tradeAccounts = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        tradeAccounts.add(userBank.getExternalTradeAccount());
                    }
                }
            }
            shareDetailQryPortfolioModels = ModelPopulateUtils.populateShareDetailQryPorefolio(list, tradeAccounts);


            /**
             * Step 2: 交易记录查询
             */
            // 查出这个用户下所有的未确认的交易记录，传ClientId
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), "", "", "", GlobalConstant.TACONFIRM_FLAG_UNCONFIRM, "", "", "", "", "",
                "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // populate applys
            ApplyListResult applyListResult = new ApplyListResult();
            List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, applyListResult, null, null);
            List<String> applyPortfolioCode = new ArrayList<>();
            for (ApplyModel applyModel : applys) {
                if (!Strings.isNullOrEmpty(applyModel.getPortfolioCode()) && !applyPortfolioCode.contains(applyModel.getPortfolioCode())) {
                    applyPortfolioCode.add(applyModel.getPortfolioCode());
                }
            }

            map = tradeConfirmQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(list);



            /**
             *  组合份额查询
             */
            //CombSharesQryDto combSharesQryDto = hsCombSharesQryService.getDto("", "", "", "", GlobalConstant.MAX_REQUEST_NUM.toString(), "1", "", user.getExternalClientId(), "1", "", "", "", "");
            CombSharesQryDto combSharesQryDto = CombSharesQryDto.builder()
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .reqry_recordsum_flag("1")
                .client_id(user.getExternalClientId())
                .qry_beginrownum("1")
                .build();
            map = hsCombSharesQryService.query(combSharesQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            List<CombSharesQryModel> combSharesQryModels = ModelPopulateUtils.populateCombSharesQry(list);
            //将同一组合的基金放在一起  存入一个map
            Map<String, List<CombSharesQryModel>> combMap = new HashMap<>();
            List<String> combCode = new ArrayList<>();
            Map<String, List<CombSharesQryModel>> combTradeMap = new HashMap<>();
            //按交易账号区分
            portfolioService.getCombTradeMap(combSharesQryModels, combTradeMap);
            //按组合累加
            portfolioService.getCombMap(combSharesQryModels, combMap, combCode);

            //组合中基金的收益
            //Map<String, List<IncomeModel>> mapIncome = getCombIncome(combTradeMap, user);

            //组合投资查询
            List<PortfolioQryModel> portfolioQryModels = getPortfolioQryModel(user);

            Map<String, List<PortfolioQryModel>> portfolioMap = new HashMap<>();
            List<String> portfolioCode = new ArrayList<>();
            //PortfolioQryModelByCode portfolioQryModelByCode = new PortfolioQryModelByCode();
            portfolioService.getPortfolioMap(portfolioQryModels, portfolioMap, portfolioCode);
            //Map<String, List<PortfolioQryModel>> portfolioMap = new HashMap<>();

            /**
             * Step 3: 获取T+0基金以及从聚源获取基金信息
             */
            // 获取T+0基金
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
            // 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (ShareModel shareModel : shares) {
                fundCodeList.add(shareModel.getFundCode());
            }

            List<FundNetValueResult> fundNetValueList = null;
            boolean gliDataException = false;
            try {
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, DateUtils.addDay(new Date(), -1));
            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryAssets - fundDetailService - Exception:", ex);
                gliDataException = true;
            }

            // 如果没有找到聚源的，返回错误
            if (gliDataException || fundNetValueList == null) {
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                return result;
            }

            // 获取上一工作日净值
            Map<String, BigDecimal> lastNetValues = new HashMap<>();
            for (FundNetValueResult netValueResult :
                fundNetValueList) {
                if (netValueResult != null) {
                    if (!lastNetValues.containsKey(netValueResult.getFundCode())) {
                        if (netValueResult.getTradingDay() == null) {
                            continue;
                        }
                        BigDecimal lastUnitValue = getLastUnitValue(netValueResult.getFundCode(), netValueResult.getTradingDay());
                        lastNetValues.put(netValueResult.getFundCode(), lastUnitValue);
                    }
                }
            }
            /**
             * Step 5: 计算结果值
             */
            //计算组合份额，资产 日收益
            List<PortfolioModel> portfolioModels = new ArrayList<>();

            //按交易账号 区分开的分红列表
            Map<String, List<DividendModel>> dividendMap = new HashMap<>();
            for (String key : combTradeMap.keySet()) {
                getDividends(key, user, dividendMap);
            }

//            CombIncomeQryDto combIncomeQryDto = CombIncomeQryDto.builder()
//                .request_num(GlobalConstant.MAX_REQUEST_NUM)
//                .reqry_recordsum_flag("1")
//                .sort_direction("1")
//                .client_id(user.getExternalClientId())
//                .qry_beginrownum("1")
//                .build();
//            map = hsCombIncomeQryService.query(combIncomeQryDto);
//            list = new ArrayList<>();
//            HundsunUtils.ConvertToList(map, list, result);
//            if (!result.getSuccess()) {
//                return result;
//            }
//            Map<String, CombIncomeQryModel> modelMap = ModelPopulateUtils.populateCombIncomeQry(list);

            for (String code : combCode) {
                //logger.info("combCode = {}", code);
                if (combMap.get(code) == null || combMap.get(code).isEmpty()) {
                    continue;
                }

                PortfolioModel portfolioModel = new PortfolioModel();
                portfolioModel = CalculateAssetsUtils.extractPortfolio(code, lookups, combMap.get(code), fundNetValueList, lastNetValues, portfolioMap, shareDetailQryPortfolioModels,  dividendMap, confirms);
                if (portfolioModel.getEnableShare() == null && portfolioModel.getCurrentShare() == null){
                    continue;
                }

                //满足调仓条件

                // 验证组合是否存在
                List<PortfolioQryModel> fundList = fundCombinationService.queryPorfolio(portfolioModel.getPortfolioCode());
                logger.debug("组合列表:{}",fundList);
                logger.debug("可用份额:{}",portfolioModel.getEnableShare());

                // 统计调仓数据
                stocks(portfolioModel, fundList, userId);


                //PortfolioModel portfolioModel = CalculateAssetsUtils.extractPortfolio(code, lookups, combMap.get(code), fundNetValueList, lastNetValues, portfolioMap, shareDetailQryPortfolioModels,null, combIncomeQryModels.get(0), dividendMap);
                Integer sellInTransit = CalculateAssetsUtils.numOfSellInTransitPortfolio(applys, lookups, null, portfolioModel.getPortfolioCode());
                portfolioModel.setSellInTransit(sellInTransit);
                portfolioModels.add(portfolioModel);
            }
//            //计算组合份额，资产 日收益   按交易账号区分
//            List<PortfolioModel> portfolioTradeModels = new ArrayList<>();
//            for (String key : map.keySet()) {
//                if (combTradeMap.get(key).size() != 0) {
//                    continue;
//                }
//                PortfolioModel portfolioModel = ModelPopulateUtils.extractPortfolio(combTradeMap.get(key).get(0).getPortfolioCode(), lookups, combTradeMap.get(key), fundNetValueList, lastNetValues, portfolioMap, applys);
//                portfolioTradeModels.add(portfolioModel);
//            }
            //将组合存入资产页面组合模型中  并计算出组合的总资产和当日总收益
            result.setPortfolios(portfolioModels);
            //result.setPortfoliosByTrade(portfolioTradeModels);
            BigDecimal portfolioWorthValue = new BigDecimal(0);
            BigDecimal portfolioTodayIncome = new BigDecimal(0);
            BigDecimal portfolioAccumIncome = new BigDecimal(0);
            List<String> portfolioTradeAccounts = new ArrayList<>();
            AssetPortfolioModel assetPortfolioForInTransit = CalculateAssetsUtils.extractInTransit(lookups, applys, applyPortfolioCode);
            for (PortfolioModel portfolioModel : portfolioModels) {
                portfolioTradeAccounts.add(portfolioModel.getTradeAcco());

                portfolioWorthValue = MoneyUtils.add(portfolioWorthValue, portfolioModel.getPortfolioWorthValue());
                portfolioTodayIncome = MoneyUtils.add(portfolioTodayIncome, portfolioModel.getPortfolioTodayIncome());
                portfolioAccumIncome = MoneyUtils.add(portfolioAccumIncome, portfolioModel.getPortfolioAccumIncome());
                result.setNetValueDate(portfolioModel.getNetValueDate());
            }

            //累计收益加上全赎    和分红
            //需要将全赎的交易账号提取出来  用于获取这个交易账号的 全部分红

            portfolioAccumIncome = CalculateAssetsUtils.getPortfolioAccumIncome(confirms,portfolioTradeAccounts, portfolioAccumIncome,dividendMap);


            result.setPortfolioAccumIncome(portfolioAccumIncome);
            result.setInTransitAmount(assetPortfolioForInTransit.getInTransitAmount());
            result.setInTransit(assetPortfolioForInTransit.isInTransit());
            result.setInTransitNum(assetPortfolioForInTransit.getInTransitNum());
            result.setPortfolioTodayIncome(portfolioTodayIncome);
            result.setPortfolioWorthValue(MoneyUtils.add(portfolioWorthValue, assetPortfolioForInTransit.getInTransitAmount()));
            result.setSuccess(true);
            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryAssets - Exception:", exception);
            throw new SystemDBException("queryAssets Failure!", exception);
        }
    }

    @Override
    public void stocks(PortfolioModel portfolioModel, List<PortfolioQryModel> fundList, Long userId) {
        if (fundList.isEmpty() || portfolioModel.getEnableShare() == null || portfolioModel.getEnableShare().compareTo(BigDecimal.valueOf(0)) == 0) {
            //调仓 0：不可调 1：待调仓 2：调仓中
            portfolioModel.setStockUp(0);
        } else {
            //满足调仓条件
            PortfolioQryModel portfolioQryModel = fundList.get(0);
            String innerPortfolioCode = portfolioQryModel.getInnerCombcode();
            //根据begin-date 和 end-date检索组合数据
            List<FundWarehouseTransfer> fundWarehouseTransfers = fundWarehouseTransferDao.queryByTime(portfolioModel.getPortfolioCode());
            if (!fundWarehouseTransfers.isEmpty()) {
                FundWarehouseTransfer fundWarehouseTransfer = fundWarehouseTransfers.get(0);
                // validate exist relation
                // 是否有正在调仓的数据
                Long inTransferCount = PageHelper.count(() -> {
                    FundPortfolioTransfer record = FundPortfolioTransfer.builder()
                        .portfolioCode(fundWarehouseTransfer.getCombinationCode())
                        .innerPortfolioCode(innerPortfolioCode)
                        .transferId(fundWarehouseTransfer.getWarehouseId())
                        .userId(userId)
                        .status(FundPortfolioTransferStatusEnum.APPLY_SUC.getKey())
                        .build();
                    fundPortfolioTransferDao.select(record);
                });
                // 是否有调仓成功的数据
                Long transferSucCount = PageHelper.count(() -> {
                    FundPortfolioTransfer record = FundPortfolioTransfer.builder()
                        .portfolioCode(fundWarehouseTransfer.getCombinationCode())
                        .innerPortfolioCode(innerPortfolioCode)
                        .transferId(fundWarehouseTransfer.getWarehouseId())
                        .userId(userId)
                        .status(FundPortfolioTransferStatusEnum.SUCCESS.getKey())
                        .build();
                    fundPortfolioTransferDao.select(record);
                });

                if (transferSucCount == 0 && inTransferCount == 0) {
                    //有可调仓数据 0：不可调 1：待调仓 2：调仓中
                    portfolioModel.setStockUp(1);
                    portfolioModel.setTransferId(fundWarehouseTransfer.getWarehouseId());
                } else if (inTransferCount > 0) {
                    //调仓中 0：不可调 1：待调仓 2：调仓中
                    portfolioModel.setStockUp(2);
                    portfolioModel.setTransferId(fundWarehouseTransfer.getWarehouseId());
                } else {
                    //调仓 0：不可调 1：待调仓 2：调仓中
                    portfolioModel.setStockUp(0);
                }


            }else {
                portfolioModel.setStockUp(0);
            }
        }

    }

    @Override
    public PortfolioModel queryPortfolioFunds(Long userId, String portfolioCode) throws BaseException {
        try {

            /**
             * Step 0: 验证数据
             */
            // 结果
            PortfolioModel result = new PortfolioModel();
            // 获取用户
            User user = userDao.getEntity(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                return result;
            }

            /**
             * Step 1: 资产查询
             */
            // 查询该用户下的所有份额
            Map<String, Object> map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "", "1", "", "", "",
                user.getExternalClientId(), "", "", null);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            ShareListResult shareListResult = new ShareListResult();
            List<ShareModel> shares = ModelPopulateUtils.populateShare(list, null, shareListResult);


            //份额明细
            ShareDetailQryDto shareDetailQryDto = ShareDetailQryDto.builder()
                .qry_beginrownum("1")
                .begin_date(DateUtils.formatDate4(new Date()))
                .client_id(user.getExternalClientId())
                .end_date(DateUtils.formatDate4(new Date()))
                .reqry_recordsum_flag("1")
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .sort_direction("1")
                .build();
            map = hsShareDetailQryService.query(shareDetailQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            logger.debug("list.size:{}", list.size());
            List<UserBank> userBankList = userBankDao.getListByUserId(userId);
            //组合
            List<ShareDetailQryModel> shareDetailQryPortfolioModels = new ArrayList<>();
            List<String> tradeAccounts = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        tradeAccounts.add(userBank.getExternalTradeAccount());
                    }
                }
            }
            shareDetailQryPortfolioModels = ModelPopulateUtils.populateShareDetailQryPorefolio(list, tradeAccounts);

            /**
             *  组合份额查询
             */
            //CombSharesQryDto combSharesQryDto = hsCombSharesQryService.getDto("", "", "", "", GlobalConstant.MAX_REQUEST_NUM.toString(), "1", "", user.getExternalClientId(), "1", "", "", "", "");
            CombSharesQryDto combSharesQryDto = CombSharesQryDto.builder()
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .reqry_recordsum_flag("1")
                .client_id(user.getExternalClientId())
                .qry_beginrownum("1")
                .build();
            map = hsCombSharesQryService.query(combSharesQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            List<CombSharesQryModel> combSharesQryModels = ModelPopulateUtils.populateCombSharesQry(list);
            //将同一组合的基金放在一起  存入一个map
            Map<String, List<CombSharesQryModel>> combMap = new HashMap<>();
            Map<String, List<CombSharesQryModel>> combTradeMap = new HashMap<>();
            List<String> combCode = new ArrayList<>();
            //按交易账号区分
            portfolioService.getCombTradeMapByPortfolioCode(combSharesQryModels, combTradeMap, portfolioCode);
            //按组合累加
            portfolioService.getCombMapByPortfolioCode(combSharesQryModels, combMap, combCode, portfolioCode);

            //
            //Map<String, List<IncomeModel>> mapIncome = getCombIncome(combTradeMap, user);
            //组合投资查询

            //PortfolioQryDto portfolioQryDto = portfolioQryService.getRequestDto(GlobalConstant.MAX_REQUEST_NUM.toString(), "1", "1", "", "", user.getExternalClientId(), "1");
//

            //组合投资查询
            List<PortfolioQryModel> portfolioQryModels = getPortfolioQryModel(user);
            Map<String, List<PortfolioQryModel>> portfolioMap = new HashMap<>();
            List<String> portfolioCodes = new ArrayList<>();
            //PortfolioQryModelByCode portfolioQryModelByCode = new PortfolioQryModelByCode();
            portfolioService.getPortfolioMap(portfolioQryModels, portfolioMap, portfolioCodes);
            //Map<String, List<PortfolioQryModel>> portfolioMap = new HashMap<>();

            //交易确认 用于 计算累计收益
            map = tradeConfirmQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(list);


            /**
             * Step 3: 获取T+0基金以及从聚源获取基金信息
             */
            // 获取T+0基金
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
            // 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (ShareModel shareModel : shares) {
                fundCodeList.add(shareModel.getFundCode());
            }

            List<FundNetValueResult> fundNetValueList = null;
            boolean gliDataException = false;
            try {
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, DateUtils.addDay(new Date(), -1));
            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryAssets - fundDetailService - Exception:", ex);
                gliDataException = true;
            }

            // 如果没有找到聚源的，返回错误
            if (gliDataException || fundNetValueList == null) {
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                return result;
            }

            // 获取上一工作日净值
            Map<String, BigDecimal> lastNetValues = new HashMap<>();
            for (FundNetValueResult netValueResult :
                fundNetValueList) {
                if (netValueResult != null) {
                    if (!lastNetValues.containsKey(netValueResult.getFundCode())) {
                        if (netValueResult.getTradingDay() == null) {
                            continue;
                        }
                        BigDecimal lastUnitValue = getLastUnitValue(netValueResult.getFundCode(), netValueResult.getTradingDay());
                        lastNetValues.put(netValueResult.getFundCode(), lastUnitValue);
                    }
                }
            }

//            //用当前组合的交易账号去查询份额接口 来获取当前组合每只基金的成本
//
//            map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, combMap.get(portfolioCode).get(0).getTradeAcco(), "1", "", "", "",
//                user.getExternalClientId(), "", "", null);
//
//            // 查询结果
//            list = new ArrayList<>();
//            HundsunUtils.ConvertToList(map, list, result);
//            if (!result.getSuccess()) {
//                return result;
//            }
//
//            // populate
//            List<ShareModel> portfolioShares = ModelPopulateUtils.populateShare(list, null, shareListResult);

//            CombIncomeQryDto combIncomeQryDto = CombIncomeQryDto.builder()
//                .request_num(GlobalConstant.MAX_REQUEST_NUM)
//                .reqry_recordsum_flag("1")
//                .sort_direction("1")
//                .client_id(user.getExternalClientId())
//                .qry_beginrownum("1")
//                .ext_combcode(portfolioCode)
//                .build();
//            map = hsCombIncomeQryService.query(combIncomeQryDto);
//            list = new ArrayList<>();
//            HundsunUtils.ConvertToList(map, list, result);
//            if (!result.getSuccess()) {
//                return result;
//            }
//            Map<String, CombIncomeQryModel> modelMap = ModelPopulateUtils.populateCombIncomeQry(list);


            /**
             * Step 5: 计算结果值
             */
            //按交易账号 区分开的分红列表
            Map<String, List<DividendModel>> dividendMap = new HashMap<>();
            for (String key : combTradeMap.keySet()) {
                getDividends(key, user, dividendMap);
            }

            //计算组合份额，资产 日收益

            result = CalculateAssetsUtils.extractPortfolio(portfolioCode, lookups, combMap.get(portfolioCode), fundNetValueList, lastNetValues, portfolioMap, shareDetailQryPortfolioModels, dividendMap, confirms);


            //满足调仓条件

            // 验证组合是否存在
            List<PortfolioQryModel> fundList = fundCombinationService.queryPorfolio(result.getPortfolioCode());
            // 统计调仓数据
            stocks(result, fundList, userId);



            //result= CalculateAssetsUtils.extractPortfolio(portfolioCode, lookups, combMap.get(portfolioCode), fundNetValueList, lastNetValues, portfolioMap, shareDetailQryPortfolioModels,portfolioShares,combIncomeQryModels.get(0), dividendMap);
            result = CalculateAssetsUtils.extractPortfolioByTrade(combTradeMap, lookups, result, fundNetValueList);
            if (result.getEnableShare().compareTo(BigDecimal.valueOf(0)) == 1 && result.getStockUp() != 2) {
                result.setCanSell(true);
            } else {
                result.setCanSell(false);
            }
            for (AssetFundModel assetFundModel : result.getPortfolioFunds()) {
                FundModel fundModel = this.queryFund(assetFundModel.getFundCode());
                if (!fundModel.getCanSell()) {
                    result.setCanSell(false);
                    break;
                }
            }
            result.setSuccess(true);
            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryAssets - Exception:", exception);
            throw new SystemDBException("queryAssets Failure!", exception);
        }
    }


    /**
     * 累加分红 - add by Shangdu Lin - 20170612
     *
     * @param clientId
     * @param fundCode
     * @return
     */
    private BigDecimal sumOfDividends(String clientId, String fundCode) {
        /**
         * Step 1: 分红明细查询
         */
        // 查询该用户下的所有份额
        Map<String, Object> map = dividendsQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", "", clientId, fundCode);

        // 查询结果
        BaseResult result = new BaseResult();
        List<Map<String, Object>> list = new ArrayList<>();
        HundsunUtils.ConvertToList(map, list, result);
        for (Map<String, Object> map1 : list) {
            //logger.info("恒生返回 分红明细 基金编码：{}，红利金额：{}",map1.get(HSOutputParameterNames.FUND_CODE), map1.get(HSOutputParameterNames.DIVIDEND_TOTALBALA));
        }
        if (!result.getSuccess()) {
            throw new BaseException(result.getErrorCode(), result.getErrorMessage());
        }

        // populate
        BaseListResult baseListResult = new BankListResult();
        List<DividendModel> dividends = ModelPopulateUtils.populateDividend(list, baseListResult);
        if (CollectionUtils.isEmpty(dividends)) {
            return null;
        }

        /**
         * Step 2: 累加分红
         */
        BigDecimal totalDividends = new BigDecimal(0);
        for (DividendModel dividend :
            dividends) {
            totalDividends = MoneyUtils.add(totalDividends, dividend.getTotalDividendBalance());
            //logger.info("分红：{}", dividend.getTotalDividendBalance());
        }

        return totalDividends;
    }

//    /**
//     * 获取某一用户当前工作日未T+1确认实时成功的快速赎回的份额
//     *
//     * @param userId
//     * @param currentDate
//     * @return
//     */
//    @Override
//    public Map<String, BigDecimal> getCurrentWorkingDayFastRedeemShares(Long userId, Date currentDate,
//                                                                        boolean isWorkingDate) throws BaseException {
//        try {
//
//            Date beginDate = new Date();
//            Date endDate = new Date();
//
//            if (!isWorkingDate) {
//                WorkingDayResult workingDaysResult = workingDateService.getWorkingDays(currentDate);
//                if (!workingDaysResult.getSuccess()) {
//                    throw new BaseException(workingDaysResult.getErrorCode(), workingDaysResult.getErrorMessage());
//                }
//
//                List<Date> workingDays = workingDaysResult.getWorkingDates();
//                if (workingDays.size() == 0) {
//                    throw new BaseException(ErrorConstant.ERROR_WORKING_DATE_NO_FIND);
//                }
//
//                beginDate = workingDays.get(0);
//                endDate = workingDays.get(0);
//                if (workingDays.size() > 1) {
//                    endDate = workingDays.get(1);
//                }
//            } else {
//                beginDate = currentDate;
//                endDate = currentDate;
//            }
//
//            ApplyListResult applyListResult = queryApplys(userId, beginDate, endDate,
//                GlobalConstant.FUND_BUSIN_CODE_T0_FAST_APPLY_SELL, GlobalConstant.TACONFIRM_FLAG_REAL_SUCCESS,
//                true);
//            if (!applyListResult.getSuccess()) {
//                throw new BaseException(applyListResult.getErrorCode(), applyListResult.getErrorMessage());
//            }
//
//            return CalculateAssetsUtils.getFastSellShares(applyListResult.getApplys());
//        } catch (BaseException baseException) {
//            throw baseException;
//        } catch (Exception exception) {
//            ExceptionUtil.logError(logger, "getFastRedeemShares - Exception:", exception);
//            throw new SystemDBException("getFastRedeemShares Failure!", exception);
//        }
//    }

//    /**
//     * 当前净值日期的上一工作日未T+1确认实时成功的快速赎回的份额
//     *
//     * @param userId
//     * @param currentDate
//     * @return
//     * @throws BaseException
//     */
//    @Override
//    public Map<String, BigDecimal> getLastWorkingDayFastRedeemShares(Long userId, Date currentDate)
//        throws BaseException {
//        try {
//            WorkingDayResult workingDaysResult = workingDateService.getLastWorkingDay(currentDate);
//            if (!workingDaysResult.getSuccess()) {
//                throw new BaseException(workingDaysResult.getErrorCode(), workingDaysResult.getErrorMessage());
//            }
//
//            Date lastWorkingDate = workingDaysResult.getLastWorkingDay();
//
//            ApplyListResult applyListResult = queryApplys(userId, lastWorkingDate, lastWorkingDate,
//                GlobalConstant.FUND_BUSIN_CODE_T0_FAST_APPLY_SELL, GlobalConstant.TACONFIRM_FLAG_REAL_SUCCESS,
//                true);
//            if (!applyListResult.getSuccess()) {
//                throw new BaseException(applyListResult.getErrorCode(), applyListResult.getErrorMessage());
//            }
//
//            return CalculateAssetsUtils.getFastSellShares(applyListResult.getApplys());
//        } catch (BaseException baseException) {
//            throw baseException;
//        } catch (Exception exception) {
//            ExceptionUtil.logError(logger, "getFastRedeemShares - Exception:", exception);
//            throw new SystemDBException("getFastRedeemShares Failure!", exception);
//        }
//    }

//    /**
//     * 计算出前后30天未T+1确认的实时成功的快速赎回的份额 - Shangdu Lin
//     * 如果要精确计算的话，只要算当前工作日以及上一工作日的未T+1确认的实时成功的快速赎回的份额
//     *
//     * @param userId
//     * @param currentDate
//     * @param interval
//     * @return
//     * @throws BaseException
//     */
//    @Override
//    public Map<String, BigDecimal> getFastRedeemSharesWithInterval(Long userId, Date currentDate, Integer interval)
//        throws BaseException {
//        try {
//
//            Date beginDate = DateUtils.addDay(currentDate, interval * (-1));
//            Date endDate = DateUtils.addDay(currentDate, interval);
//
//            ApplyListResult applyListResult = queryApplys(userId, beginDate, endDate,
//                GlobalConstant.FUND_BUSIN_CODE_T0_FAST_APPLY_SELL, GlobalConstant.TACONFIRM_FLAG_REAL_SUCCESS,
//                true);
//            if (!applyListResult.getSuccess()) {
//                throw new BaseException(applyListResult.getErrorCode(), applyListResult.getErrorMessage());
//            }
//
//            return CalculateAssetsUtils.getFastSellShares(applyListResult.getApplys());
//        } catch (BaseException baseException) {
//            throw baseException;
//        } catch (Exception exception) {
//            ExceptionUtil.logError(logger, "getFastRedeemShares - Exception:", exception);
//            throw new SystemDBException("getFastRedeemShares Failure!", exception);
//        }
//    }

    // 获取基金的风险等级
    @Override
    public NewsModel getRiskLevel(String fundCode) throws BaseException {
        logger.info("getRiskLevel - Fund Code:" + fundCode);
        try {

            // 基金的风险等级查询
            Map<String, Object> map = newsQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", fundCode, "",
                "", "");

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            BaseResult result = new BaseResult();
            // 检查转换结果
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                throw new BaseException(result.getErrorCode(), result.getErrorMessage(), result.getErrorType());
            }

            NewsModel model = new NewsModel();
            // 设置默认值
            model.setRiskLevel(-1);
            model.setMinShare(new BigDecimal(0));
            if (list != null && list.size() > 0) {
                Map<String, Object> item = list.get(0);
                String riskLevel = item.get(HSOutputParameterNames.OFUND_RISKLEVEL).toString();
                if (!StringUtil.isEmpty(riskLevel)) {
                    model.setRiskLevel(Integer.parseInt(riskLevel));
                }
                model.setMinShare(
                    BigDecimalUtil.ConvertToBigDecimal(item.get(HSOutputParameterNames.EN_MINSHARE).toString()));
                model.setFundType(item.get(HSOutputParameterNames.OFUND_TYPE).toString());
            } else {
                logger.error("getRiskLevel - Fund Code:" + fundCode + ", no risk level found");
            }

            return model;

        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "getRiskLevel - Exception:", exception);
            throw new SystemDBException("getRiskLevel Failure!", exception);
        }
    }

    // 获取主交易账号
    @Override
    public AccountModel getMainAccount(String clientId) throws BaseException {
        logger.info("getMainAccount - client Id:" + clientId);
        try {

            // 查询TA账户信息
            Map<String, Object> map = accountQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "", "", clientId,
                "", "", "");

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            BaseResult result = new BaseResult();
            // 检查转换结果
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                throw new BaseException(result.getErrorCode(), result.getErrorMessage(), result.getErrorType());
            }

            // populate
            BaseListResult listResult = new BaseListResult();
            List<AccountModel> accounts = ModelPopulateUtils.populateAccount(list, listResult);

            for (AccountModel accountModel : accounts) {
                if (accountModel.getMainAccountFlag().equals(GlobalConstant.MAIN_ACCOUNT_FLAG_YES)) {
                    return accountModel;
                }
            }

        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "getMainAccount - Exception:", exception);
            throw new SystemDBException("getMainAccount Failure!", exception);
        }

        return null;
    }

    /**
     * 查询基金的交易限制 - Shangdu Lin - 20170330
     *
     * @param fundCode
     * @return
     */
    @Override
    public List<TradeLimitModel> queryTradeLimits(String fundCode) {
        return queryTradeLimits(fundCode, null, null, null);
    }

    @Override
    public List<TradeLimitModel> queryTradeLimits(String fundCode, boolean isRedemptionPurchase) {
        return queryTradeLimits(fundCode, null, null, null, isRedemptionPurchase);
    }

    /**
     * 获取基金的交易限制 - Shangdu Lin - 20170330
     *
     * @param fundCode
     * @param capitalMode
     * @param businCode
     * @param customType
     * @return
     */
    @Override
    public List<TradeLimitModel> queryTradeLimits(String fundCode, String capitalMode, String businCode,
                                                  String customType) {
        // 如果没有找到份额类别，则直接返回
        ShareTypeModel shareTypeModel = getShareType(fundCode);
        if (shareTypeModel == null) {
            return null;
        }

        // 如果没传资金方式，则从字典表中获取开户时使用的资金方式
        if (StringUtil.isEmpty(capitalMode)) {
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_CAPITALMODE);
            if (CollectionUtils.isEmpty(lookups)) {
                return null;
            }

            for (Lookup item :
                lookups) {
                // 获取开户时使用的资金方式
                String flag = item.getComponentValue();
                if (flag != null && flag.equals("1")) {
                    capitalMode = item.getRuleValue();
                }
            }
        }

        //如果未找到资金方式，则直接返回
        if (StringUtil.isEmpty(capitalMode)) {
            return null;
        }

        // 如果没有配置交易类别，则默认查出所有的交易类别的交易限制
        if (StringUtil.isEmpty(businCode)) {
            businCode = "";
        }

        //如果没有传入客户类别，则默认查询个人客户的交易限制
        if (StringUtil.isEmpty(customType)) {
            customType = GlobalConstant.CUST_TYPE_PERSONAL;
        }

        TradeLimitListResult limits = queryTradeLimits(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1",
            fundCode, shareTypeModel.getShareType(), capitalMode, businCode, customType);
        if (!limits.getSuccess()) {
            return null;
        }

        return limits.getLimits();
    }

    public List<TradeLimitModel> queryTradeLimits(String fundCode, String capitalMode, String businCode,
                                                  String customType, boolean isRedemptionPurchase) {
        // 如果没有找到份额类别，则直接返回
        ShareTypeModel shareTypeModel = getShareType(fundCode);
        if (shareTypeModel == null) {
            return null;
        }

        // 如果没传资金方式，则从字典表中获取开户时使用的资金方式
        if (StringUtil.isEmpty(capitalMode)) {
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_CAPITALMODE);
            if (CollectionUtils.isEmpty(lookups)) {
                return null;
            }

            for (Lookup item :
                lookups) {
                // 获取开户时使用的资金方式
                String flag = item.getComponentValue();
                if (flag != null && flag.equals("1")) {
                    capitalMode = item.getRuleValue();
                }
            }
        }

        //如果未找到资金方式，则直接返回
        if (StringUtil.isEmpty(capitalMode)) {
            return null;
        }

        // 如果没有配置交易类别，则默认查出所有的交易类别的交易限制
        if (StringUtil.isEmpty(businCode)) {
            businCode = "";
        }

        //如果没有传入客户类别，则默认查询个人客户的交易限制
        if (StringUtil.isEmpty(customType)) {
            customType = GlobalConstant.CUST_TYPE_PERSONAL;
        }

        TradeLimitListResult limits = queryTradeLimits(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1",
            fundCode, shareTypeModel.getShareType(), capitalMode, businCode, customType, isRedemptionPurchase);
        if (!limits.getSuccess()) {
            return null;
        }

        return limits.getLimits();
    }


    @Override
    public TradeLimitListResult queryTradeLimits(Integer requestNum, Integer queryFlag, Integer beginNum,
                                                 String sortDirection, String fundCode, String shareType,
                                                 String capitalMode, String businCode,
                                                 String customType) throws BaseException {
        logger.info("fundCode={}, shareType={}, capitalMode={}, businCode={}, customType={}",
            fundCode, shareType, capitalMode, businCode, customType);

        TradeLimitListResult result = new TradeLimitListResult();
        try {

            // 交易限制查询
            // 资金方式之前因为直销系统只配了柜台的交易限制，没有配易宝的交易限制，所以传了空，不限制渠道
            Map<String, Object> map = tradeLimitQueryService.query(requestNum, queryFlag, beginNum, sortDirection,
                fundCode, shareType, capitalMode, businCode, customType);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<TradeLimitModel> limits = ModelPopulateUtils.populateTradeLimit(list);

            /**
             * 最低起购金额由后台控制，不走恒生
             */
            Lookup lookup = lookupService.getLookup(RuleConstant.RULE_LOOKUP_TYPE_T0FUND, fundCode);

            if (lookup != null) {
                String ruleLimitValue = lookup.getRuleLimitValue();

                if (com.codi.bus.util.LookupUtil.isJSONObject(ruleLimitValue)) {
                    JSONObject father = JSONObject.parseObject(ruleLimitValue);
                    JSONObject child = null;
                    if (customType.equals(GlobalConstant.CUST_TYPE_PERSONAL)) {
                        child = father.getJSONObject("user");
                    }else if (customType.equals(GlobalConstant.CUST_TYPE_ORGANIZATION)) {
                        child = father.getJSONObject("org");
                    }
                    if (child != null) {
                        String minMoney = child.getString("minMoney");
                        String reminder = child.getString("reminder");
                        for(TradeLimitModel item: limits){
                            if(StringUtils.isNotBlank(minMoney)){
                                try {
                                    BigDecimal bigDecimal =  new BigDecimal(minMoney);
                                    item.setMinValue(bigDecimal);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }

                            }
                            item.setReminder(reminder);
                        }
                    }
                }
            }

            NewsModel newsModel = getRiskLevel(fundCode);
            if (newsModel != null) {
                for (TradeLimitModel tradeLimitModel : limits) {
                    tradeLimitModel.setMinHoldShares(newsModel.getMinShare());
                }
            }

            // 新建新接口来获取银行卡的交易限额
            // 获取支行渠道对银行卡的交易限额
            /*
             * List<Lookup> lookups=lookupDao.getLookupByType(RuleConstant.
             * RULE_LOOKUP_TYPE_EPAYBANK); List<BankLimitModel> bankLimits=new
             * ArrayList<>(); for (Lookup lookup : lookups) { BankLimitModel
             * model=new BankLimitModel();
             * model.setBankName(lookup.getRuleText());
             * model.setBankCode(lookup.getRuleValue());
             * model.setSingleTradeLimit(BigDecimalUtil.ConvertToBigDecimal(
             * lookup.getComponentValue(), new BigDecimal(-1)));
             *
             * bankLimits.add(model); }
             */

            result.setSuccess(true);
            result.setLimits(limits);
            // result.setBankLimits(bankLimits);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryTradeLimits - Exception:", exception);
            throw new SystemDBException("queryTradeLimits Failure!", exception);
        }
    }

    public TradeLimitListResult queryTradeLimits(Integer requestNum, Integer queryFlag, Integer beginNum,
                                                 String sortDirection, String fundCode, String shareType,
                                                 String capitalMode, String businCode,
                                                 String customType, boolean isRedemptionPurchase) throws BaseException {
        logger.info("fundCode={}, shareType={}, capitalMode={}, businCode={}, customType={}",
            fundCode, shareType, capitalMode, businCode, customType);

        TradeLimitListResult result = new TradeLimitListResult();
        try {

            // 交易限制查询
            // 资金方式之前因为直销系统只配了柜台的交易限制，没有配易宝的交易限制，所以传了空，不限制渠道
            Map<String, Object> map = tradeLimitQueryService.query(requestNum, queryFlag, beginNum, sortDirection,
                fundCode, shareType, capitalMode, businCode, customType);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<TradeLimitModel> limits = ModelPopulateUtils.populateTradeLimit(list);

            NewsModel newsModel = getRiskLevel(fundCode);
            if (newsModel != null) {
                for (TradeLimitModel tradeLimitModel : limits) {
                    tradeLimitModel.setMinHoldShares(newsModel.getMinShare());
                }
            }

            // 新建新接口来获取银行卡的交易限额
            // 获取支行渠道对银行卡的交易限额
            /*
             * List<Lookup> lookups=lookupDao.getLookupByType(RuleConstant.
             * RULE_LOOKUP_TYPE_EPAYBANK); List<BankLimitModel> bankLimits=new
             * ArrayList<>(); for (Lookup lookup : lookups) { BankLimitModel
             * model=new BankLimitModel();
             * model.setBankName(lookup.getRuleText());
             * model.setBankCode(lookup.getRuleValue());
             * model.setSingleTradeLimit(BigDecimalUtil.ConvertToBigDecimal(
             * lookup.getComponentValue(), new BigDecimal(-1)));
             *
             * bankLimits.add(model); }
             */

            result.setSuccess(true);
            result.setLimits(limits);
            // result.setBankLimits(bankLimits);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryTradeLimits - Exception:", exception);
            throw new SystemDBException("queryTradeLimits Failure!", exception);
        }
    }

    // 交易限制查询
    @Override
    public TradeLimitModel getTradeLimit(String fundCode, String businCode, String shareType, String customType,
                                         String capitalMode) throws BaseException {
        logger.info("queryTradeLimits - fundCode={}, shareType={}, capitalMode={}, businCode={}, customType={}",
            fundCode, shareType, capitalMode, businCode, customType);

        // 调用恒生接口
        // 资金方式之前因为直销系统只配了柜台的交易限制，没有配易宝的交易限制，所以传了空，不限制渠道
        Map<String, Object> map = tradeLimitQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", fundCode,
            shareType, capitalMode, businCode, customType);

        // 查询结果
        List<Map<String, Object>> list = new ArrayList<>();
        BaseResult result = new BaseResult();
        // 检查转换结果
        HundsunUtils.ConvertToList(map, list, result);
        if (!result.getSuccess()) {
            throw new BaseException(result.getErrorCode(), result.getErrorMessage(), result.getErrorType());
        }

        // populate
        List<TradeLimitModel> limits = ModelPopulateUtils.populateTradeLimit(list);



        NewsModel newsModel = getRiskLevel(fundCode);
        if (newsModel != null) {
            for (TradeLimitModel tradeLimitModel : limits) {
                tradeLimitModel.setMinHoldShares(newsModel.getMinShare());
            }
        }

        if (limits != null && limits.size() > 0) {
            return limits.get(0);
        }

        return null;
    }

    @Override
    public BaseListResult queryAccountBanks(String tradeAccount, String clientId, String capitalMode,
                                            Integer requestNum, Integer queryFlag, Integer beginNum, String sortDirection) throws BaseException {
        logger.info("queryAccountBanks - tradeAccount={}, clientId={}, capitalMode={}", tradeAccount, clientId, capitalMode);
        BankListResult result = new BankListResult();
        try {

            // 根据恒生银行编号获取银行名称
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);

            // 客户银行帐号查询
            Map<String, Object> map = accountBankQueryService.query(requestNum, queryFlag, beginNum, sortDirection,
                tradeAccount, clientId, capitalMode);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<BankModel> banks = ModelPopulateUtils.populateBank(list, lookups, result);

            // 获取TA账户信息
            TAListResult taListResult = queryTAAccounts(clientId);
            if (!taListResult.getSuccess()) {
                return taListResult;
            }
            List<AccountModel> accountModels = taListResult.getAccounts();

            // 获取交易账号状态Lookup
            List<Lookup> tradeAccountStatusLookup = lookupDao
                .getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_TDACCSTATUS);

            // 获取TA账号状态Lookup
            // List<Lookup> taAccountStatusLookup =
            // lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_TAACCSTATUS);

            // 过滤掉相同交易账号不同TA账号的银行卡
            Iterator<BankModel> iterator = banks.iterator();
            Map<String, String> dictionary = new HashMap<>();
            while (iterator.hasNext()) {
                BankModel model = iterator.next();
                String key = MessageFormat.format("{0}|{1}", model.getUserBankNo(), model.getCapitalMode());
                if (dictionary.containsKey(key)) {
                    iterator.remove();
                } else {
                    dictionary.put(key, key);

                    if (accountModels != null) {
                        for (AccountModel accountModel : accountModels) {
                            if (accountModel.getTradeAccount().equals(model.getTradeAccount())) {
                                model.setMainAccountFlag(accountModel.getMainAccountFlag());
                                model.setTradeAccountStatus(accountModel.getTradeAccountStatus());
                                model.setTaAccount(accountModel.getTaAccount());
                                model.setTradeAccountStatusString(LookupUtil.getText(tradeAccountStatusLookup,
                                    accountModel.getTradeAccountStatus()));
                                /*
                                 * model.setTaAccountStatus(accountModel.
                                 * getTaAccountStatus());
                                 * model.setTaAccountStatusString(
                                 * LookupUtil.getText(taAccountStatusLookup,
                                 * accountModel.getTaAccountStatus()));
                                 */
                            }
                        }
                    }
                }
            }

            result.setSuccess(true);
            result.setBanks(banks);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryAccountBanks - Exception:", exception);
            throw new SystemDBException("queryAccountBanks Failure!", exception);
        }
    }

    @Override
    public ShareTypeListResult queryShareTypes(Integer requestNum, Integer queryFlag, Integer beginNum,
                                               String sortDirection, String fundCode, String fundName) throws BaseException {

        logger.info("queryShareTypes - fundCode={}, fundName={}", fundCode, fundName);

        ShareTypeListResult result = new ShareTypeListResult();

        // 查询份额类别接口
        Map<String, Object> map = shareTypeQueryService.query(requestNum, queryFlag, beginNum, sortDirection, fundCode,
            fundName);

        // 查询结果
        List<Map<String, Object>> list = new ArrayList<>();
        HundsunUtils.ConvertToList(map, list, result);
        if (!result.getSuccess()) {
            return result;
        }

        List<ShareTypeModel> shareTypes = ModelPopulateUtils.populateShareType(list);

        result.setSuccess(true);
        result.setShareTypes(shareTypes);

        return result;
    }

    // 获取ShareType
    @Override
    public ShareTypeModel getShareType(String fundCode) throws BaseException {

        logger.info("getShareType - fundCode=" + fundCode);

        ShareTypeListResult result = queryShareTypes(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", fundCode, "");
        if (!result.getSuccess()) {
            logger.error("getShareType - fundCode=" + fundCode + ", no fund type found");
            return null;
        }

        List<ShareTypeModel> shareTypes = result.getShareTypes();
        if (shareTypes != null && shareTypes.size() > 0) {
            return shareTypes.get(0);
        }

        return null;
    }

    // 查询某一笔交易
    @Override
    public ApplyModel queryApply(String applyNo) throws BaseException {
        logger.info("queryApply - applyNo=" + applyNo);

        ApplyListResult result = new ApplyListResult();

        // 查询某一笔交易
        Map<String, Object> map = tradeApplyQueryService.query(1, 1, 1, "1", "", "", "", "", applyNo, "", "", "", "",
            "", "", "", "", "");

        // 查询结果
        List<Map<String, Object>> list = new ArrayList<>();
        HundsunUtils.ConvertToList(map, list, result);
        if (!result.getSuccess()) {
            logger.warn("queryApply - applyNo=" + applyNo + ", no apply trade order found");
            return null;
        }

        // 交易业务名称
        List<Lookup> tradeLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
        // 银行名称
        List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
        // populate
        List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, result, tradeLookups, bankLookups);
        if (applys != null && applys.size() > 0) {
            return applys.get(0);
        }

        return null;
    }


    @Override
    public ApplyModel queryPortfolioApply(String portfolioApplyNo) {
        logger.info("portfolioApplyNo={}", portfolioApplyNo);

        ApplyListResult result = new ApplyListResult();

        // 查询某一笔交易
        TradeApplyQryDto dto = new TradeApplyQryDto();
        dto.setRequest_num("1");
        dto.setReqry_recordsum_flag("1");
        dto.setQry_beginrownum("1");
        dto.setSort_direction("1");
        dto.setPortfolio_allot_no(portfolioApplyNo);


        Map<String, Object> map = tradeApplyQueryService.query(dto);

        // 查询结果
        List<Map<String, Object>> list = new ArrayList<>();
        HundsunUtils.ConvertToList(map, list, result);
        if (!result.getSuccess()) {
            logger.warn("queryApply - applyNo={}, no apply trade order found", portfolioApplyNo);
            return null;
        }

        // 交易业务名称
        List<Lookup> tradeLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
        // 银行名称
        List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
        // populate
        List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, result, tradeLookups, bankLookups);
        if (applys != null && applys.size() > 0) {
            return applys.get(0);
        }

        return null;
    }

    @Override
    public ApplyListResult queryApplys(Long userId, Integer requestNum, Integer queryFlag, Integer beginNum,
                                       String sortDirection, boolean filterT0) throws BaseException {
        logger.info("queryApplys - userId=" + userId);

        ApplyListResult result = new ApplyListResult();
        try {

            // 获取用户
            User user = userDao.getEntity(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            // 查出这个用户下所有的交易记录，包括已确认和未确认的，传ClientId
//            Map<String, Object> map = tradeApplyQueryService.query(requestNum, queryFlag, beginNum, sortDirection, "",
//                user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "");
//
//            // 查询结果
//            List<Map<String, Object>> list = new ArrayList<>();
//            HundsunUtils.ConvertToList(map, list, result);
//            if (!result.getSuccess()) {
//                return result;
//            }

            //这里应该只需要查询非组合基金的交易记录  目前非组合基金的交易记录应该需要通过传tradeAccount来区分
            List<Map<String, Object>> list = new ArrayList<>();
            Map<String, Object> map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "");


            // 查询结果
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // 交易业务名称
            List<Lookup> tradeLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
            // 银行名称
            List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            // populate
            List<ApplyModel> fundApplys = ModelPopulateUtils.populateApply2(list, result, tradeLookups, bankLookups, user.getIsOrg());

            //过滤组合基金

            List<ApplyModel> applys = new ArrayList<>();
            for (ApplyModel applyModel : fundApplys) {
                //赎回转购的业务辅助标识
                if (GlobalConstant.BUSIN_ASS_CODE_CURRENCY_SEEL_THEN_BUY.equals(applyModel.getBusinAssCode())) {
                    if (!Strings.isNullOrEmpty(applyModel.getOriginalAppno())) {
                        for (ApplyModel apply : fundApplys) {
                            if (applyModel.getOriginalAppno().equals(apply.getAllotNo())) {
                                applyModel.setBusinAssName(apply.getFundCode());
                            }
                        }
                    }
                }
                //过滤组合 记录
                if (Strings.isNullOrEmpty(applyModel.getPortfolioCode())) {
                    applys.add(applyModel);
                }
            }
            // 把T0基金过滤掉，因为T0基金有专门的页面来显示
            filterT0(applys, filterT0);

            // 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (ApplyModel shareModel : applys) {
                if (!Strings.isNullOrEmpty(shareModel.getBusinAssName()) && !fundCodeList.contains(shareModel.getBusinAssName())) {
                    fundCodeList.add(shareModel.getBusinAssName());
                }
                if (!fundCodeList.contains(shareModel.getFundCode())) {
                    fundCodeList.add(shareModel.getFundCode());
                }
                shareModel.setIsOrg(user.getIsOrg());
            }

            List<FundNetValueResult> fundNetValueList = null;
            try {
                // 获取聚源数据
                for (String fund : fundCodeList) {
                }
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, new Date());

            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryApplys - fundDetailService - Exception:", ex);
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                return result;
            }

            Collections.sort(applys, new Comparator<ApplyModel>() {
                @Override
                public int compare(ApplyModel o1, ApplyModel o2) {
                    String o1No = o1.getAllotNo(), o2No = o2.getAllotNo();
                    if (o1No.length() < o2No.length()) {
                        return 1;
                    } else if (o1No.length() > o2No.length()) {
                        return -1;
                    }
                    return 0 - o1No.compareTo(o2No);
                }
            });
            // 如果没有找到聚源数据，则默认为空数组
            if (fundNetValueList == null) {
                fundNetValueList = new ArrayList<>();
            }

            // 判断是否有在途的交易
            boolean inTransit = false;
            for (ApplyModel model : applys) {
                // 未确认并且不能是无效扣款状态
                if (model.getTAConfirmFlag().equals(GlobalConstant.TACONFIRM_FLAG_UNCONFIRM)
                    && !model.getDeductStatus().equals(GlobalConstant.DEDUCT_STATUS_INVALID)) {
                    inTransit = true;
                }
                // 设置基金信息
                for (FundNetValueResult fundNetValue : fundNetValueList) {
                    if (fundNetValue == null) {
                        continue;
                    }
                    if (fundNetValue.getFundCode().equals(model.getFundCode())) {
                        model.setFundName(fundNetValue.getChiNameAbbr());
                    }
                    if (fundNetValue.getFundCode().equals(model.getBusinAssName())) {
                        model.setBusinAssName("瑞富宝-" + fundNetValue.getChiNameAbbr());
                    }
//                    if (model.getBusinAssName() != null && model.getBusinAssName().contains("瑞富宝") && !Strings.isNullOrEmpty(model.getFundName())){
//                        break;
//                    }
                }

            }

            // 是否有在途的交易
            result.setInTransit(inTransit);
            result.setSuccess(true);
            result.setTotalCount((long) applys.size());
            List<ApplyModel> applysReal = new ArrayList<>();
            for (int i = 1; i <= applys.size(); i++) {
                if (i >= beginNum && i < requestNum + beginNum) {
                    applysReal.add(applys.get(i - 1));
                }
            }
            result.setApplys(applysReal);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryApplys - Exception:", exception);
            throw new SystemDBException("queryApplys Failure!", exception);
        }
    }

    @Override
    public PortfolioApplyListResult queryPortfolioApplys(Long userId, Integer requestNum, Integer queryFlag, Integer beginNum,
                                                         String sortDirection, boolean filterT0) throws BaseException {
        logger.info("queryApplys - userId=" + userId);

        PortfolioApplyListResult result = new PortfolioApplyListResult();
        try {

            // 获取用户
            User user = userDao.getEntity(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            // 查出这个用户下所有的交易记录，包括已确认和未确认的，传ClientId
            Map<String, Object> map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "");

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
//            Date currentWorkingDate = workingDateService.getWorkingDay(new Date());
//            CollectionResult<ConfirmModel> collectionResult = queryTradeConfirms(userId, GlobalConstant.MAX_REQUEST_NUM, 1, 1,
//                "1", "", "", currentWorkingDate, currentWorkingDate, "");
//            if (!collectionResult.getSuccess()) {
//                result.setSuccess(false);
//                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
//                result.setErrorCode(collectionResult.getErrorCode());
//                result.setErrorMessage(collectionResult.getErrorMessage());
//                return result;
//            }

            // 交易业务名称
            List<Lookup> tradeLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
            // 银行名称
            List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            // populate
            List<PortfolioApplyModel> applys = ModelPopulateUtils.populatePortfolioApply(list, result, tradeLookups, bankLookups, user.getIsOrg());
//            // 把T0基金过滤掉，因为T0基金有专门的页面来显示
//            filterT0(applys, filterT0);
            List<ApplyModel> applys2 = ModelPopulateUtils.populateApply2(list, result, tradeLookups, bankLookups, user.getIsOrg());

            map = tradeConfirmQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(list);


            // 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (ApplyModel shareModel : applys) {
                if (!fundCodeList.contains(shareModel.getFundCode())) {
                    fundCodeList.add(shareModel.getFundCode());
                }
                shareModel.setIsOrg(user.getIsOrg());
            }

            /**
             * 从聚源获取基金信息
             */
            List<FundNetValueResult> fundNetValueList = null;
            try {
                // 获取聚源数据
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, new Date());

            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryApplys - fundDetailService - Exception:", ex);
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                return result;
            }

            // 如果没有找到聚源数据，则默认为空数组
            if (fundNetValueList == null) {
                fundNetValueList = new ArrayList<>();
            }

            Map<String, PortfolioApplyModel> modelMap = new HashMap<>();

            /*
             * 将相同组合中的记录合并     并过滤非组合的记录
             */
            for (PortfolioApplyModel portfolioApplyModel : applys) {
                //过滤非组合 记录
                if ("".equals(portfolioApplyModel.getPortfolioCode())) {
                    continue;
                }
                //如果map 中已经有这个集合  就把金额和份额相加 并向二级列表中加入一条记录
                else if (modelMap.containsKey(portfolioApplyModel.getPortfolioAllotNo())) {
                    modelMap.get(portfolioApplyModel.getPortfolioAllotNo()).setBalance(MoneyUtils.add(modelMap.get(portfolioApplyModel.getPortfolioAllotNo()).getBalance(), portfolioApplyModel.getBalance()));
                    modelMap.get(portfolioApplyModel.getPortfolioAllotNo()).setShares(MoneyUtils.add(modelMap.get(portfolioApplyModel.getPortfolioAllotNo()).getShares(), portfolioApplyModel.getShares()));
                    //如果是 43或者06  说明是 组合调仓   要把业务辅助编码设置为 43或者06  用于后面的判断  其他地方并不会用到这个业务辅助编码
                    if (GlobalConstant.BUSIN_ASS_CODE_CURRENCY_SEEL_THEN_BUY.equals(portfolioApplyModel.getBusinAssCode()) || GlobalConstant.BUSIN_ASS_CODE_SELL_THEN_BUY_CURRENCY.equals(portfolioApplyModel.getBusinAssCode())){
                        modelMap.get(portfolioApplyModel.getPortfolioAllotNo()).setBusinAssCode(portfolioApplyModel.getBusinAssCode());
                    }
                    //modelMap.get(portfolioApplyModel.getPortfolioAllotNo()).getFundApplys().add(portfolioApplyModel);
                } else {
                    modelMap.put(portfolioApplyModel.getPortfolioAllotNo(), portfolioApplyModel);
//                    List<PortfolioApplyModel> portfolioApplyModels = new ArrayList<>();
//                    modelMap.get(portfolioApplyModel.getPortfolioAllotNo()).setFundApplys(portfolioApplyModels);
//                    modelMap.get(portfolioApplyModel.getPortfolioAllotNo()).getFundApplys().add(portfolioApplyModel);
                }
            }

            List<PortfolioApplyModel> portfolioApplys = new ArrayList<>();
            for (String key : modelMap.keySet()) {
                portfolioApplys.add(modelMap.get(key));
            }

            /**
             * 设置是否机构  并创建二级记录  组合中具体基金的交易记录
             */
            for (PortfolioApplyModel portfolioApplyModel : portfolioApplys) {
                if (Strings.isNullOrEmpty(portfolioApplyModel.getProtocolName())) {
                    for (ApplyModel applyModel : applys) {
                        if ("022".equals(applyModel.getFundBusinCode()) && portfolioApplyModel.getTradeAcco().equals(applyModel.getTradeAcco())) {
                            portfolioApplyModel.setProtocolName(applyModel.getProtocolName());
                        }
                    }
                }
                if(GlobalConstant.BUSIN_ASS_CODE_CURRENCY_SEEL_THEN_BUY.equals(portfolioApplyModel.getBusinAssCode()) || GlobalConstant.BUSIN_ASS_CODE_SELL_THEN_BUY_CURRENCY.equals(portfolioApplyModel.getBusinAssCode())){
                    portfolioApplyModel.setFundBusinName(GlobalConstant.PORTFOLIO_TRANSFER);
                    portfolioApplyModel.setIsTransfer(true);
                }else {
                    portfolioApplyModel.setIsTransfer(false);
                }
                List<ApplyModel> applyModels = new ArrayList<>();
                portfolioApplyModel.setFundApplys(applyModels);
                portfolioApplyModel.setIsOrg(user.getIsOrg());
                //logger.info("组合名1={}, ",portfolioApplyModel.getProtocolName());

            }



            /**
             * 添加组合信息  并加入组合中基金的具体信息
             */
            // 判断是否有在途的交易
            boolean inTransit = false;
            for (PortfolioApplyModel portfolioApplyModel : portfolioApplys) {
                // 未确认并且不能是无效扣款状态
                if (portfolioApplyModel.getTAConfirmFlag().equals(GlobalConstant.TACONFIRM_FLAG_UNCONFIRM)
                    && !portfolioApplyModel.getDeductStatus().equals(GlobalConstant.DEDUCT_STATUS_INVALID)) {
                    inTransit = true;
                }

                for (ApplyModel apply : applys2) {
                    if (portfolioApplyModel.getPortfolioAllotNo().equals(apply.getPortfolioAllotNo())) {
                        for (ConfirmModel confirmModel : confirms) {
                            if (apply.getAllotNo().equals(confirmModel.getAllotNo())) {
                                apply.setShares(confirmModel.getConfirmShares());
                                apply.setBalance(confirmModel.getConfirmBalance());
                                break;
                            }
                        }
                        // 设置基金信息
                        for (FundNetValueResult fundNetValue : fundNetValueList) {
                            if (fundNetValue == null) {
                                continue;
                            }
                            if (fundNetValue.getFundCode().equals(apply.getFundCode())) {
                                apply.setFundName(fundNetValue.getChiNameAbbr());
                                break;
                            }
                        }
                        apply.setStatusString(ModelPopulateUtils.getStatusString(apply));
                        portfolioApplyModel.getFundApplys().add(apply);
                    }
                }


                /*
                 * 新增组合调仓的状态判断 和 普通的申购赎回区分
                 * zx 2018年3月21日10:14:30
                 */

                boolean confirm = false;
                boolean success = false;
                boolean fail = false;
                if (GlobalConstant.PORTFOLIO_TRANSFER.equals(portfolioApplyModel.getFundBusinName())){
                    for (ApplyModel applyModel : portfolioApplyModel.getFundApplys()) {
                        if (applyModel.getTAConfirmFlag().equals(GlobalConstant.TACONFIRM_FLAG_UNCONFIRM)){
                            confirm = true;
                        }
                        if (applyModel.getTAConfirmFlag().equals(GlobalConstant.TACONFIRM_FLAG_CONFIRMED)){
                            success = true;
                        }
                        if (applyModel.getTAConfirmFlag().equals(GlobalConstant.TACONFIRM_FLAG_FAIL)){
                            fail = true;
                        }
                    }
                    if (fail){
                        portfolioApplyModel.setStatusString("确认失败");
                    }else if (confirm){
                        portfolioApplyModel.setStatusString("申请成功");
                    }else if (success){
                        portfolioApplyModel.setStatusString("确认成功");
                    }else {
                        portfolioApplyModel.setStatusString("确认失败");
                    }
                }else {
                    //zx 2018年3月28日11:28:53  这块代码针对 部分成功部分失败
                    for (ApplyModel applyModel : portfolioApplyModel.getFundApplys()) {
                        if (ModelPopulateUtils.getStatusString(applyModel).contains("成功")) {
                            portfolioApplyModel.setFundCode(applyModel.getFundCode());
                            portfolioApplyModel.setAllotNo(applyModel.getAllotNo());
                            portfolioApplyModel.setOrderDate(applyModel.getOrderDateReal());
                            portfolioApplyModel.setOrderTime(applyModel.getOrderTimeReal());
                            portfolioApplyModel.setBankCode(applyModel.getBankCode());
                            portfolioApplyModel.setBankHSCode(applyModel.getBankHSCode());
                            portfolioApplyModel.setBankName(applyModel.getBankName());
                            portfolioApplyModel.setDeductStatus(applyModel.getDeductStatus());
                            portfolioApplyModel.setCapitalMode(applyModel.getCapitalMode());
                            portfolioApplyModel.setTAConfirmFlag(applyModel.getTAConfirmFlag());
                            portfolioApplyModel.setFundBusinCode(applyModel.getFundBusinCode());
                            portfolioApplyModel.setFundBusinName(applyModel.getFundBusinNameReal());
                            portfolioApplyModel.setSendFlag(applyModel.getSendFlag());
                            portfolioApplyModel.setApplyDate(applyModel.getApplyDateReal());

                            break;
                        }
                    }
                    portfolioApplyModel.setStatusString(ModelPopulateUtils.getStatusString(portfolioApplyModel));
                }

                //logger.info("组合名2={}, ",portfolioApplyModel.getProtocolName());
            }

//            /*
//            组合调仓时  在还未发起申购时展示申购交易记录
//             */
//            for (PortfolioApplyModel portfolioApplyModel : portfolioApplys) {
//
//            }



//            for (PortfolioApplyModel portfolioApplyModel:portfolioApplys){
//                ModelPopulateUtils.getStatusString(portfolioApplyModel);
//            }
            // 是否有在途的交易
            result.setInTransit(inTransit);
            result.setSuccess(true);
            // 按日期倒叙排序
            Collections.sort(portfolioApplys, new Comparator<PortfolioApplyModel>() {

                @Override
                public int compare(PortfolioApplyModel o1, PortfolioApplyModel o2) {
                    String o1No = o1.getAllotNo(), o2No = o2.getAllotNo();
                    return 0 - o1No.compareTo(o2No);
                }
            });
            result.setTotalCount(Long.valueOf(portfolioApplys.size()));
            List<PortfolioApplyModel> portfolioApplysReal = new ArrayList<>();
            for (int i = 1; i <= portfolioApplys.size(); i++) {
                if (i >= beginNum && i < requestNum + beginNum) {
                    portfolioApplysReal.add(portfolioApplys.get(i - 1));
                }
            }
            result.setApplys(portfolioApplysReal);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryApplys - Exception:", exception);
            throw new SystemDBException("queryApplys Failure!", exception);
        }
    }

//    @Override
//    public PortfolioSummaryResult portfolioApplys(Long userId, Integer requestNum, Integer queryFlag, Integer beginNum, String sortDirection, boolean filterT0) throws BaseException {
//        logger.info("queryApplys - userId=" + userId);
//
//        PortfolioSummaryResult result = new PortfolioSummaryResult();
//        try {
//
//            // 获取用户
//            User user = userDao.getEntity(userId);
//
//            // 判断是否已开户
//            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
//                result.setSuccess(false);
//                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
//                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
//                return result;
//            }
//
//            // 查出这个用户下所有的交易记录，包括已确认和未确认的，传ClientId
//            PortfolioSummaryQryDto portfolioSummaryQryDto = portfolioSummaryQryService.getDto("",requestNum.toString(),queryFlag.toString(),beginNum.toString(),sortDirection,"","",user.getExternalClientId(),"","","","");
//
//            Map<String, Object> map = portfolioSummaryQryService.query(portfolioSummaryQryDto);
//
//            // 查询结果
//            List<Map<String, Object>> list = new ArrayList<>();
//            HundsunUtils.ConvertToList(map, list, result);
//            if (!result.getSuccess()) {
//                return result;
//            }
//
//            // 交易业务名称
//            List<Lookup> tradeLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
//            // 银行名称
//            List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
//
//            List<PortfolioSummaryQryModel> portfolioSummaryQryModels = ModelPopulateUtils.portfolioApply(list,result,tradeLookups,bankLookups,user.getIsOrg());
//
//            result.setPortfolioSummaryQryModels(portfolioSummaryQryModels);
//            result.setSuccess(true);
//
//
//
//            return result;
//        } catch (BaseException baseException) {
//            throw baseException;
//        } catch (Exception exception) {
//            ExceptionUtil.logError(logger, "queryApplys - Exception:", exception);
//            throw new SystemDBException("queryApplys Failure!", exception);
//        }
//    }

    /**
     * 查询某只基金
     *
     * @param fundCode
     * @return
     */
    @Override
    public FundModel queryFund(String fundCode) {

        FundListResult result = new FundListResult();

        // 基金查询
        Map<String, Object> map = fundQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1",
            fundCode);

        // 查询结果
        List<Map<String, Object>> list = new ArrayList<>();
        HundsunUtils.ConvertToList(map, list, result);
        if (!result.getSuccess()) {
            throw new BaseException(result.getErrorCode(), result.getErrorMessage(), result.getErrorType());
        }

        // populate
        List<FundModel> funds = ModelPopulateUtils.populateFund(list, result);
        if (CollectionUtils.isEmpty(funds)) {
            logger.error("queryFund: Fund Code - {}, 未能找到该只基金");
            return null;
        }

        return funds.get(0);
    }

    @Override
    public FundListResult queryFunds(Integer requestNum, Integer queryFlag, Integer beginNum, String sortDirection,
                                     String fundCode) throws BaseException {
        logger.info("queryFunds - fundCode=" + fundCode);

        FundListResult result = new FundListResult();
        try {

            // 基金查询
            Map<String, Object> map = fundQueryService.query(requestNum, queryFlag, beginNum, sortDirection, fundCode);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<FundModel> funds = ModelPopulateUtils.populateFund(list, result);

            // 获取T+0基金
            List<Lookup> T0Funds = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
            // 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (FundModel model : funds) {
                // 判断是否为T0基金
                model.setIsT0(ModelPopulateUtils.isT0Fund(T0Funds, model.getFundCode()));
                // 设置基金风险等级
                NewsModel newsModel = getRiskLevel(model.getFundCode());
                model.setRiskLevel(newsModel.getRiskLevel());

                fundCodeList.add(model.getFundCode());
            }
            List<FundPerformanceResult> fundNetValueList = null;
            try {
                fundNetValueList = fundDetailService.getFundPerformanceByBatch(fundCodeList);
            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryFunds - fundDetailService - Exception:", ex);
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                return result;
            }

            // 如果获取不到数据，则默认为空数组
            if (fundNetValueList == null) {
                fundNetValueList = new ArrayList<>();
            }

            for (FundModel model : funds) {
                // 设置基金信息
                for (FundPerformanceResult fundPerformanceResult : fundNetValueList) {
                    if (fundPerformanceResult == null) {
                        continue;
                    }
                    String tempCode = fundPerformanceResult.getFundCode();
                    if (!StringUtil.isEmpty(tempCode)) {
                        if (tempCode.equals(model.getFundCode())) {
                            // 近三月收益
                            BigDecimal recentRateInThreeMonth = fundPerformanceResult.getRRInThreeMonth();
                            if (recentRateInThreeMonth == null) {
                                recentRateInThreeMonth = new BigDecimal(0);
                            }
                            // 7日年化收益率
                            BigDecimal latestWeeklyYield = fundPerformanceResult.getLatestWeeklyYield();
                            if (latestWeeklyYield == null) {
                                latestWeeklyYield = new BigDecimal(0);
                            }

                            model.setLatestWeeklyYield(latestWeeklyYield);
                            model.setThreeMonthRate(recentRateInThreeMonth);
                            model.setFundName(fundPerformanceResult.getChiNameAbbr());
                            // 设置前端显示的基金类型
                            model.setAppFundType(fundPerformanceResult.getFundTypeCode());
                            model.setFundType(fundPerformanceResult.getFundType());

                            break;
                        }
                    }
                }
                if (model.getThreeMonthRate() == null) {
                    model.setThreeMonthRate(new BigDecimal(0));
                }
                if (model.getLatestWeeklyYield() == null) {
                    model.setLatestWeeklyYield(new BigDecimal(0));
                }
            }

            // 过滤掉被排除的基金类型
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_EXCLUDE_FUND_TYPE);
            Iterator<FundModel> iterator = null;
            if (ListUtil.isNotEmpty(lookups)) {
                iterator = funds.iterator();
                while (iterator.hasNext()) {
                    FundModel model = iterator.next();
                    for (Lookup lookup : lookups) {
                        String fundType = String.valueOf(model.getAppFundType());
                        if (lookup.getRuleValue().equals(fundType)) {
                            iterator.remove();
                        }
                    }
                }
            }

            // 过滤掉被排除的基金代码
            lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_EXCLUDE_FUND_CODE);
            if (ListUtil.isNotEmpty(lookups)) {
                iterator = funds.iterator();
                while (iterator.hasNext()) {
                    FundModel model = iterator.next();
                    for (Lookup lookup : lookups) {
                        if (lookup.getRuleValue().equals(model.getFundCode())) {
                            iterator.remove();
                        }
                    }
                }
            }

            result.setSuccess(true);
            result.setFunds(funds);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryFunds - Exception:", exception);
            throw new SystemDBException("queryFunds Failure!", exception);
        }

    }

    /*
     * 根据交易确认的记录来算购买某个基金的本金，可以按以下来计算： Step 1:
     * 过滤出确认标记taConfirm_Flag为1和2的记录，即确认成功和部分确认的记录 Step 2:
     * 把所有确认业务编码fund_busin_code为122相加，即申购的相加 Step 3:
     * 把所有确认业务编码fund_busin_code为120相加，即认购的相加 Step 4:
     * 减去所有确认业务编码fund_busin_code为124，即减去赎回的记录
     */
    // TODO 如果有快速赎回业务，则需要另外考虑，快速赎回是钱实时到账，份额则需要申请，T+1减份额
    @Override
    public ShareListResult queryShares(Long userId, Integer requestNum, Integer queryflag, Integer beginNum,
                                       String sortDirection, boolean filterT0) throws BaseException {
        logger.info("queryShares - userId=" + userId);

        ShareListResult result = new ShareListResult();
        try {

            // Step 1: 获取用户
            User user = userDao.getEntity(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            // 根据恒生银行编号获取银行名称
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);

            // 查询当前工作日
            Date currentWorkingDate = workingDateService.getWorkingDay(new Date());

            // Step 2: 资产查询, 查询该用户下的所有份额
            Map<String, Object> map = shareQueryService.query(requestNum, queryflag, beginNum, "", sortDirection, "",
                "", "", user.getExternalClientId(), "", "", null);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ShareModel> shares = ModelPopulateUtils.populateShare(list, lookups, result);
            // 把T0基金过滤掉，因为T0基金有专门的页面来显示
            if (filterT0) {
                List<Lookup> T0Funds = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
                Iterator<ShareModel> iterator = shares.iterator();
                while (iterator.hasNext()) {
                    ShareModel model = iterator.next();
                    if (ModelPopulateUtils.isT0Fund(T0Funds, model.getFundCode())) {
                        iterator.remove();
                    }
                }
            }

            // Step 3: 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (ShareModel shareModel : shares) {
                fundCodeList.add(shareModel.getFundCode());
            }

            List<FundNetValueResult> fundNetValueList = null;
            try {
                Date t = maxDate();
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, t);
            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryShares - fundDetailService - Exception:", ex);
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                return result;
            }

            // 如果没有找到聚源的，则默认为空
            if (fundNetValueList == null) {
                fundNetValueList = new ArrayList<>();
            }

            // Step 4: 从恒生获取交易确认信息
            // 入参为交易账号的话，只查询该交易账号下的交易, 如果为客户编号的话，则查询该客户编号下的所有交易账号的交易
            Map<String, Object> confirmMap = tradeConfirmQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1",
                user.getExternalClientId(), "", "");

            // 查询结果
            List<Map<String, Object>> mapList = new ArrayList<>();
            HundsunUtils.ConvertToList(confirmMap, mapList, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(mapList);

            // Step 5: 根据数据计算
            // 总持仓收益
            BigDecimal totalAccumIncome = new BigDecimal(0);
            // 总本金
            BigDecimal totalFundBalance = new BigDecimal(0);
            // 总资产
            BigDecimal totalWorthValue = new BigDecimal(0);
            // 总当日收益
            BigDecimal totalTodayIncome = new BigDecimal(0);
            // 保存上一工作日净值
            Map<String, BigDecimal> netValues = new HashedMap();
            for (ShareModel shareModel : shares) {

                // 更新基金
                // 设置净值默认值
                shareModel.setNetValue(new BigDecimal(0));
                // 设置今日收益默认值
                shareModel.setTodayIncome(new BigDecimal(0));
                // 设置今日收益率默认值
                shareModel.setTodayIncomeRate(new BigDecimal(0));
                // 设置基金信息
                for (FundNetValueResult fundNetValue : fundNetValueList) {
                    // 聚源没有找到该只基金
                    if (fundNetValue == null || StringUtil.isEmpty(fundNetValue.getFundCode())) {
                        continue;
                    }
                    // 以聚源的数据为准
                    if (fundNetValue.getFundCode().equals(shareModel.getFundCode())) {
                        shareModel.setFundName(fundNetValue.getChiNameAbbr());
                        shareModel.setNetValue(fundNetValue.getUnitNV());
                        shareModel.setNetValueDate(fundNetValue.getTradingDay());
                        // 设置前端显示的基金类型
                        shareModel.setAppFundType(fundNetValue.getFundTypeCode());
                        if (fundNetValue.getFundTypeCode() != null) {
                            /**
                             * Shangdu Lin - 20170419
                             * 非货币基金、货币基金：如果净值日期小于当前工作日，则不计算确认日期是当前工作日的基金份额
                             */
                            BigDecimal currentShare = shareModel.getCurrentShare();
                            if (CalculateAssetsUtils.dateBefore(fundNetValue.getTradingDay(), currentWorkingDate)) {
                                BigDecimal confirmedShares = CalculateAssetsUtils.getConfirmShares(confirms, shareModel
                                    .getFundCode(), currentWorkingDate);
                                currentShare = MoneyUtils.subtract(currentShare, confirmedShares);
                            }
                            if (fundNetValue.getFundTypeCode() == FundConst.FUND_TYPE_CODE_CURRENCY) {
                                // 日收益=当前份额/10000*万份之收益
                                // TODO:有申购时：日收益=当前份额/10000*万份之收益
                                // 有赎回时: 日收益=当前份额/10000*万份之收益
                                // 日收益
                                BigDecimal dailyProfit = fundNetValue.getDailyProfit();
                                if (dailyProfit == null) {
                                    dailyProfit = new BigDecimal(0);
                                    logger.error("queryShares-getDailyProfit is null for fund code ={}", shareModel.getFundCode());
                                }

                                BigDecimal todayIncome = currentShare.divide(new BigDecimal(10000), 4)
                                    .multiply(dailyProfit);
                                shareModel.setTodayIncome(todayIncome);
                                // 日收益率
                                if (shareModel.getWorthValue().compareTo(new BigDecimal(0)) > 0) {
                                    BigDecimal todayIncomeRate = todayIncome.divide(shareModel.getWorthValue(), 4);
                                    shareModel.setTodayIncomeRate(todayIncomeRate);
                                } else {
                                    shareModel.setTodayIncomeRate(new BigDecimal(0));
                                }
                                // 万份之收益
                                shareModel.setDailyProfit(dailyProfit);

                            } else {
                                // 日收益=当前基金份额*（当天净值- 前一个工作日净值）
                                // TODO:有申购时：日收益=当前基金份额*（当天净值- 前一个工作日净值）
                                // 有赎回时: 日收益=当前基金份额*（当天净值 - 前一个工作日净值）
                                // 日收益率
                                BigDecimal todayIncomeRate = fundNetValue.getNvDailyGrowthRate();
                                if (todayIncomeRate == null) {
                                    todayIncomeRate = new BigDecimal(0);
                                    logger.error("queryShares-getNvDailyGrowthRate is null for fund code ={}", shareModel.getFundCode());
                                }
                                shareModel.setTodayIncomeRate(todayIncomeRate);
                                // 日收益=当前基金份额*（当天净值- 前一个工作日净值）
                                BigDecimal lastUnitValue = new BigDecimal(0);
                                if (netValues.containsKey(shareModel.getFundCode())) {
                                    lastUnitValue = netValues.get(shareModel.getFundCode());
                                } else {
                                    lastUnitValue = getLastUnitValue(shareModel.getFundCode(), fundNetValue.getTradingDay());
                                    netValues.put(shareModel.getFundCode(), lastUnitValue);
                                }
                                BigDecimal margin = MoneyUtils.subtract(fundNetValue.getUnitNV(), lastUnitValue);
                                BigDecimal todayIncome = currentShare.multiply(margin);
                                shareModel.setTodayIncome(todayIncome);

                                logger.warn("queryShares - 基金代码:{},上一工作日净值:{},当前净值:{},当前份额:{},净值差额:{},日收益:{}",
                                    shareModel
                                        .getFundCode(),
                                    lastUnitValue, fundNetValue.getUnitNV(), currentShare, margin, todayIncome);
                            }
                        }

                        break;
                    }
                }

                // 更新最新的资产值
                // 资产=当前份额*净值
                BigDecimal netValue = shareModel.getNetValue();
                if (netValue == null) {
                    netValue = new BigDecimal(0);
                }
                BigDecimal worthValue = shareModel.getCurrentShare().multiply(netValue);
                shareModel.setWorthValue(worthValue);
                // 计算累计收益，即持仓收益
                BigDecimal accumIncome = new BigDecimal(0);
                BigDecimal confirmBalance = new BigDecimal(0);
                for (ConfirmModel confirmModel : confirms) {
                    String taConfirmFlag = confirmModel.getTAConfirmFlag();
                    String fundCode = confirmModel.getFundCode();
                    String businCode = confirmModel.getFundBusinCode();
                    // 只需要考虑成功的交易，即1-确认成功 2-部分确认
                    if (!taConfirmFlag.equals(GlobalConstant.TACONFIRM_FLAG_CONFIRMED)
                        && !taConfirmFlag.equals(GlobalConstant.TACONFIRM_FLAG_PART_CONFIRMED)) {
                        continue;
                    }

                    // 认购、申购的金额相加, 赎回的金额相减
                    if (fundCode.equals(shareModel.getFundCode())) {
                        if (businCode.equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_BUY)
                            || businCode.equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_SUBSCRIBE_BUY)) {
                            confirmBalance = confirmBalance.add(confirmModel.getConfirmBalance());
                        } else if (businCode.equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_SELL)) {
                            confirmBalance = confirmBalance.subtract(confirmModel.getConfirmBalance());
                        }
                    }
                }
                // 更新本金值
                shareModel.setFundBalance(confirmBalance);
                // 更新累计收益
                accumIncome = shareModel.getWorthValue().subtract(confirmBalance);
                shareModel.setAccumIncome(accumIncome);

                // 计算持仓收益率
                shareModel.setAccumIncomeRate(new BigDecimal(0));
                BigDecimal fundWorthValue = shareModel.getWorthValue();
                if (fundWorthValue != null && fundWorthValue.compareTo(new BigDecimal(0)) > 0) {
                    // 持仓收益率
                    BigDecimal accumIncomeRate = shareModel.getAccumIncome().divide(fundWorthValue, 4);
                    shareModel.setAccumIncomeRate(accumIncomeRate);
                }

                // 获取基金类型
                NewsModel newsModel = getRiskLevel(shareModel.getFundCode());
                if (newsModel != null) {
                    shareModel.setFundType(newsModel.getFundType());
                }

                totalAccumIncome = totalAccumIncome.add(shareModel.getAccumIncome());
                totalWorthValue = totalWorthValue.add(shareModel.getWorthValue());
                totalTodayIncome = totalTodayIncome.add(shareModel.getTodayIncome());
            }

            // 查出这个用户下所有的未确认的交易记录，传ClientId
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), "", "", "", GlobalConstant.TACONFIRM_FLAG_UNCONFIRM, "", "", "", "", "",
                "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // populate applys
            ApplyListResult applyListResult = new ApplyListResult();
            List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, applyListResult, null, null);
            // 把T0基金过滤掉，因为T0基金有专门的页面来显示
            filterT0(applys, filterT0);
            //总资产=确认的资产+有效在途的金额
            BigDecimal totalInTransit = CalculateAssetsUtils.sumOfInTransit(applys, lookups, false, null,null);
            totalWorthValue = MoneyUtils.add(totalWorthValue, totalInTransit);

            result.setSuccess(true);
            result.setShares(shares);
            result.setTotalInTransit(totalInTransit);
            result.setTotalAccumIncome(totalAccumIncome);
            result.setTotalFundBalance(totalFundBalance);
            result.setTotalTodayIncome(totalTodayIncome);
            result.setTotalWorthValue(totalWorthValue);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryShares - Exception:", exception);
            throw new SystemDBException("queryShares Failure!", exception);
        }
    }

    @Override
    public ShareListResult querySharesWithoutPort(Long userId, Integer requestNum, Integer queryflag, Integer beginNum,
                                                  String sortDirection, boolean filterT0) throws BaseException {
        logger.info("queryShares - userId=" + userId);

        ShareListResult result = new ShareListResult();
        try {
            // 获取T+0基金
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
            // Step 1: 获取用户
            User user = userDao.getEntity(userId);

            List<UserBank> userBankList = userBankDao.getListByUserId(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            // 根据恒生银行编号获取银行名称
            List<Lookup> lookupsBank = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);


            // 查询当前工作日
            Date currentWorkingDate = getCurrencyDate();

            // Step 2: 资产查询, 查询该用户下的所有份额
            Map<String, ShareModel> shareMap = new HashMap<>();
            List<ShareModel> normalShares = new ArrayList<>();
            //分红
            List<DividendModel> dividends = getDividend(user,userBankList);
            //收益
            //List<IncomeModel> incomeModels = getIncome(user,userBankList);
            List<Map<String, Object>> list = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    List<Map<String, Object>> listAll = new ArrayList<>();

                    for (UserBank userBank : userBankList) {
                        // 查询该用户下的所有非组合基金份额
                        Map<String, Object> map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, userBank.getExternalTradeAccount(), "1", "", "", "",
                            user.getExternalClientId(), "", "", null);
                        list = new ArrayList<>();
                        HundsunUtils.ConvertToList(map, list, result);
                        if (!result.getSuccess()) {
                            return result;
                        }
                        listAll.addAll(list);
                    }
                    normalShares = ModelPopulateUtils.populateNormalShare(listAll, lookupsBank);
                }
            }


//
            //份额明细
            ShareDetailQryDto shareDetailQryDto = ShareDetailQryDto.builder()
                .qry_beginrownum("1")
                .begin_date(DateUtils.formatDate4(new Date()))
                .client_id(user.getExternalClientId())
                .end_date(DateUtils.formatDate4(new Date()))
                .reqry_recordsum_flag("1")
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .sort_direction("1")
                .build();
            Map<String, Object> map = hsShareDetailQryService.query(shareDetailQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }


            //普通基金
            List<ShareDetailQryModel> shareDetailQryFundModels = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        shareDetailQryFundModels = ModelPopulateUtils.populateShareDetailQryFund(list, userBank);
                    }
                }
            }

            CombSharesQryDto combSharesQryDto = CombSharesQryDto.builder()
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .reqry_recordsum_flag("1")
                .client_id(user.getExternalClientId())
                .qry_beginrownum("1")
                .build();
            map = hsCombSharesQryService.query(combSharesQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            List<CombSharesQryModel> combSharesQryModels = ModelPopulateUtils.populateCombSharesQry(list);

            // 把T0基金过滤掉，因为T0基金有专门的页面来显示
            if (filterT0) {
                List<Lookup> T0Funds = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
                Iterator<ShareModel> iterator = normalShares.iterator();
                while (iterator.hasNext()) {
                    ShareModel model = iterator.next();
                    if (ModelPopulateUtils.isT0Fund(T0Funds, model.getFundCode())) {
                        iterator.remove();
                    }
                }
            }

            // Step 3: 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (ShareModel shareModel : normalShares) {
                //先过滤掉  还没有基金信息
                if (shareModel.getFundCode().equals("161728")){
                    continue;
                }
                fundCodeList.add(shareModel.getFundCode());
            }

            List<FundNetValueResult> fundNetValueList = null;
            try {
                Date t = maxDate();
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, DateUtils.addDay(new Date(), -1));
            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryShares - fundDetailService - Exception:", ex);
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                return result;
            }

            // 如果没有找到聚源的，则默认为空
            if (fundNetValueList == null) {
                fundNetValueList = new ArrayList<>();
            }

            // Step 4: 从恒生获取交易确认信息
            // 入参为交易账号的话，只查询该交易账号下的交易, 如果为客户编号的话，则查询该客户编号下的所有交易账号的交易
            Map<String, Object> confirmMap = tradeConfirmQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1",
                user.getExternalClientId(), "", "");

            // 查询结果
            List<Map<String, Object>> mapList = new ArrayList<>();
            HundsunUtils.ConvertToList(confirmMap, mapList, result);
            if (!result.getSuccess()) {
                return result;
            }


            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(mapList);



            /**
             * 交易记录
             */
            // 查出这个用户下所有的未确认的交易记录，传ClientId
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), "", "", "", GlobalConstant.TACONFIRM_FLAG_UNCONFIRM, "", "", "", "", "",
                "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // populate applys
            ApplyListResult applyListResult = new ApplyListResult();
            List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, applyListResult, null, null);
            // 把T0基金过滤掉，因为T0基金有专门的页面来显示
            filterT0(applys, filterT0);

            // Step 5: 根据数据计算
            // 总持仓收益
            BigDecimal totalAccumIncome = new BigDecimal(0);
            logger.debug("totalAccumIncome1:{}",totalAccumIncome);

            // 总本金
            BigDecimal totalFundBalance = new BigDecimal(0);
            // 总资产
            BigDecimal totalWorthValue = new BigDecimal(0);
            // 总当日收益
            BigDecimal totalTodayIncome = new BigDecimal(0);
            //总持仓收益
            BigDecimal totalShareIncome = new BigDecimal(0);
            // 保存上一工作日净值
            Map<String, BigDecimal> netValues = new HashedMap();
            // 净值日期
            Date netValueDate = DateUtils.addDay(new Date(), -1); //默认净值日期为前一天

            //基金编码集合
            List<String> fundCodes = new ArrayList<>();

            for (ShareModel shareModel : normalShares) {
                //先过滤掉  还没有基金信息
                if (shareModel.getFundCode().equals("161728")){
                    continue;
                }
                FundModel fundModel = this.queryFund(shareModel.getFundCode());
                shareModel.setFundStatus(fundModel.getFundStatus());
                fundCodes.add(shareModel.getFundCode());
                List<UserBank> userBanks = bankService.getBanks(user.getUserId(), shareModel.getBankAccount());
                UserBank userBankForBranch = new UserBank();
                if (userBanks != null && !userBanks.isEmpty()) {
                    userBankForBranch = userBanks.get(0);
                    shareModel.setBranchBank(userBankForBranch.getBranchbank() == null ? "" : userBankForBranch.getBranchbank());
                    shareModel.setBranchBankName(userBankForBranch.getBankBranchName() == null ? "" : userBankForBranch.getBankBranchName());
                }

                // 更新基金
                // 设置净值默认值
                shareModel.setNetValue(new BigDecimal(0));
                // 设置今日收益默认值
                shareModel.setTodayIncome(new BigDecimal(0));
                // 设置今日收益率默认值
                shareModel.setTodayIncomeRate(new BigDecimal(0));
                // 设置基金信息
                for (FundNetValueResult fundNetValue : fundNetValueList) {
                    // 聚源没有找到该只基金
                    if (fundNetValue == null || StringUtil.isEmpty(fundNetValue.getFundCode()) || fundNetValue.getFundTypeCode() == null) {
                        continue;
                    }
                    // 以聚源的数据为准
                    if (fundNetValue.getFundCode().equals(shareModel.getFundCode())) {
                        shareModel.setFundName(fundNetValue.getChiNameAbbr());
                        shareModel.setNetValue(fundNetValue.getUnitNV());
                        shareModel.setNetValueDate(fundNetValue.getTradingDay());
                        netValueDate = fundNetValue.getTradingDay();
                        // 设置前端显示的基金类型
                        shareModel.setAppFundType(fundNetValue.getFundTypeCode());
                        if (fundNetValue.getFundTypeCode() != null) {
                            /**
                             * Shangdu Lin - 20170419
                             * 非货币基金、货币基金：如果净值日期小于当前工作日，则不计算确认日期是当前工作日的基金份额
                             */
                            BigDecimal currentShare = shareModel.getCurrentShare();
                            //计算当日收益
                            BigDecimal oldShares = currentShare;
                            for (ShareDetailQryModel shareDetailQryModel : shareDetailQryFundModels) {
                                //logger.debug("aaaaa 确认日期：{}, 基金编码：{}， 可用份额：{}",shareDetailQryModel.getAffirmDate(),shareDetailQryModel.getFundCode(),shareDetailQryModel.getEnableShares());
                                if (shareModel.getFundCode().equals(shareDetailQryModel.getFundCode()) && shareDetailQryModel.getAffirmDate().equals(DateUtils.formatDate4(new Date())) && shareModel.getTradeAccount().equals(shareDetailQryModel.getTradeAccount())) {
                                    //logger.debug("货币基金  当前份额：{}，确认份额：{},基金Code:{}，基金名称",oldShares,shareDetailQryModel.getEnableShares(),shareModel.getFundCode(),shareModel.getFundName());
                                    oldShares = oldShares.subtract(shareDetailQryModel.getEnableShares());
                                }
                            }
                            if (fundNetValue.getFundTypeCode() == FundConst.FUND_TYPE_CODE_CURRENCY) {
                                // 日收益=当前份额/10000*万份之收益
                                // TODO:有申购时：日收益=当前份额/10000*万份之收益
                                // 有赎回时: 日收益=当前份额/10000*万份之收益
                                // 日收益
                                BigDecimal dailyProfit = fundNetValue.getDailyProfit();
                                if (dailyProfit == null) {
                                    dailyProfit = new BigDecimal(0);
                                    //logger.error("queryShares-getDailyProfit is null for fund code ={}", shareModel.getFundCode());
                                }
                                BigDecimal todayIncome = oldShares.divide(new BigDecimal(10000), 4)
                                    .multiply(dailyProfit);
                                //logger.debug("万分收益：{}，今日收益：{}，当前份额：{}，老份额：{}",dailyProfit,todayIncome,currentShare);
                                BigDecimal totalDividend = new BigDecimal(0);
                                //计算昨日收益的现金分红部分
                                /**
                                 * 昨日收益加上分红
                                 *
                                 */
                                for (DividendModel dividendModel : dividends) {
                                    if ("1".equals(dividendModel.getAutoBuy()) && shareModel.getFundCode().equals(dividendModel.getFundCode())&& dividendModel.getTradeAccount().equals(shareModel.getTradeAccount())) {
                                        if (dividendModel.getDividendDate().equals(DateUtils.formatDate4(DateUtils.addDay(new Date(), -1)))) {
                                            todayIncome = MoneyUtils.add(todayIncome, dividendModel.getDividendBalance());
                                        }
                                    }
                                }
                                todayIncome = todayIncome.setScale(2, BigDecimal.ROUND_HALF_UP);
                                shareModel.setTodayIncome(todayIncome);
                                // 日收益率
                                // 份额明细查询没有这个字段
                                if (shareModel.getWorthValue() != null) {
                                    if (shareModel.getWorthValue().compareTo(new BigDecimal(0)) > 0) {
                                        BigDecimal todayIncomeRate = todayIncome.divide(shareModel.getWorthValue(), 4);
                                        shareModel.setTodayIncomeRate(todayIncomeRate);
                                    } else {
                                        shareModel.setTodayIncomeRate(new BigDecimal(0));
                                    }
                                    // 万份之收益
                                    shareModel.setDailyProfit(dailyProfit);
                                }

                            } else {
                                // 日收益=当前基金份额*（当天净值- 前一个工作日净值）
                                // TODO:有申购时：日收益=当前基金份额*（当天净值- 前一个工作日净值）
                                // 有赎回时: 日收益=当前基金份额*（当天净值 - 前一个工作日净值）
                                // 日收益率
                                BigDecimal todayIncomeRate = fundNetValue.getNvDailyGrowthRate();
                                if (todayIncomeRate == null) {
                                    todayIncomeRate = new BigDecimal(0);
                                    logger.error("queryShares-getNvDailyGrowthRate is null for fund code ={}", shareModel.getFundCode());
                                }
                                shareModel.setTodayIncomeRate(todayIncomeRate);
                                // 日收益=当前基金份额*（当天净值- 前一个工作日净值）
                                BigDecimal lastUnitValue = new BigDecimal(0);
                                if (netValues.containsKey(shareModel.getFundCode())) {
                                    lastUnitValue = netValues.get(shareModel.getFundCode());
                                } else {
                                    lastUnitValue = getLastUnitValue(shareModel.getFundCode(), fundNetValue.getTradingDay());
                                    netValues.put(shareModel.getFundCode(), lastUnitValue);
                                }
                                BigDecimal margin = MoneyUtils.subtract(fundNetValue.getUnitNV(), lastUnitValue);
                                BigDecimal todayIncome = oldShares.multiply(margin);
                                //计算昨日收益的现金分红部分
                                /**
                                 * 昨日收益加上分红
                                 *
                                 */
                                for (DividendModel dividendModel : dividends) {
                                    if ("1".equals(dividendModel.getAutoBuy()) && shareModel.getFundCode().equals(dividendModel.getFundCode())&& dividendModel.getTradeAccount().equals(shareModel.getTradeAccount())) {
                                        if (dividendModel.getDividendDate().equals(DateUtils.formatDate4(DateUtils.addDay(new Date(), -1)))) {
                                            todayIncome = MoneyUtils.add(todayIncome, dividendModel.getDividendBalance());
                                        }
                                    }
                                }
                                todayIncome = todayIncome.setScale(2, BigDecimal.ROUND_HALF_UP);
                                shareModel.setTodayIncome(todayIncome);

                            }
                        }

                        break;
                    }
                }

                // 更新最新的资产值
                // 资产=当前份额*净值
                BigDecimal netValue = shareModel.getNetValue();
                if (netValue == null) {
                    netValue = new BigDecimal(0);
                }
                BigDecimal worthValue = shareModel.getCurrentShare().multiply(netValue);
                worthValue = MoneyUtils.add(worthValue, shareModel.getUnpaidIncome());
                shareModel.setWorthValue(worthValue);
                BigDecimal totalDividend = new BigDecimal(0);

                for (DividendModel dividendModel : dividends) {
                    if ("1".equals(dividendModel.getAutoBuy()) && shareModel.getFundCode().equals(dividendModel.getFundCode())&& dividendModel.getTradeAccount().equals(shareModel.getTradeAccount())) {
                        if (dividendModel.getDividendDate().compareTo(DateUtils.formatDate4(new Date())) < 0) {
                            totalDividend = MoneyUtils.add(totalDividend, dividendModel.getDividendBalance());
                        }
                        if (DateUtils.formatDate4(new Date()).compareTo(dividendModel.getEquityRegDate()) >= 0 && DateUtils.formatDate4(new Date()).compareTo(dividendModel.getDividendDate()) <= 0) {
                            shareModel.setDividends("该基金最近有分红配送");
                        }
                    }
                }
                if (shareModel.getDividends() == null) {
                    shareModel.setDividends("");
                }
//                //累计收益
//                if (incomeModels.size() != 0) {
//                    for (IncomeModel incomeModel : incomeModels) {
//                        if (incomeModel.getFundCode().equals(shareModel.getFundCode())&& incomeModel.getTradeAccount().equals(shareModel.getTradeAccount())) {
//                            shareModel.setTotalAccumIncome(incomeModel.getAccumIncome());
//                            //shareModel.setTotalAccumIncome(MoneyUtils.add(incomeModel.getAccumIncome(), totalDividend));
//                            break;
//                        }
//                    }
//                }

                //累计收益新算法   资产 加 赎回金额 减 申购金额 加上现金分红
                BigDecimal accumIncome = MoneyUtils.add(worthValue, totalDividend);
                for (ConfirmModel confirm :  confirms){
                    if (confirm.getFundCode().equals(shareModel.getFundCode()) && confirm.getTradeAcco().equals(shareModel.getTradeAccount())){
                        if (confirm.getTAConfirmFlag().equals(GlobalConstant.TACONFIRM_FLAG_CONFIRMED)){
                            if (confirm.getFundBusinCode().equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_BUY) || confirm.getFundBusinCode().equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_FIX_BUY)){
                                accumIncome = MoneyUtils.subtract(accumIncome, confirm.getTradeConfirmBalance());
                            }
                            if (confirm.getFundBusinCode().equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_SELL)){
                                accumIncome = MoneyUtils.add(accumIncome, confirm.getTradeConfirmBalance());
                            }
                        }
                    }
                }

                shareModel.setTotalAccumIncome(accumIncome);
                totalAccumIncome = MoneyUtils.add(totalAccumIncome, accumIncome);
                logger.debug("totalAccumIncome1:{}",totalAccumIncome);
                //赎回在途笔数
                Integer sellInTransit = CalculateAssetsUtils.numSellOfInTransit(applys, lookups, false, shareModel.getFundCode(), shareModel.getTradeAccount());
                shareModel.setSellInTransit(sellInTransit);


//                // 计算持仓收益率
//                shareModel.setAccumIncomeRate(new BigDecimal(0));
//                BigDecimal fundWorthValue = shareModel.getWorthValue();
//                if (fundWorthValue != null && fundWorthValue.compareTo(new BigDecimal(0)) > 0) {
//                    // 持仓收益率
//                    BigDecimal accumIncomeRate = shareModel.getShareIncome().divide(fundWorthValue, 4);
//                    shareModel.setAccumIncomeRate(accumIncomeRate);
//                }

                // 获取基金类型
                NewsModel newsModel = getRiskLevel(shareModel.getFundCode());
                if (newsModel != null) {
                    shareModel.setFundType(newsModel.getFundType());
                    shareModel.setRiskLevel(newsModel.getRiskLevel());
                    shareModel.setRiskLevelString(newsModel.getRiskLevelString());
                }

                SecurityProduct product = fundDetailService.getSecurityProduct(shareModel.getFundCode());

                if (product == null) {
                    logger.error("Security Product is not exist");
                    result.setErrorCode(ErrorConstant.ERROR_FUND_NOT_EXIST);
                    result.setSuccess(false);
                    result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
                    return result;
                }

                // 七日年化率
                FundMMYieldPerformance perf = fundMMYieldPerformanceService.getFundMMYieldPerformance(product.getInnerCode());
                if (perf != null && perf.getLatestWeeklyYield() != null ){
                    shareModel.setLatestWeeklyYield(MoneyUtils.formatMoney(perf.getLatestWeeklyYield(), 2));// 最近7日折算年收益率（%）
                }else{
                    shareModel.setLatestWeeklyYield("");
                }

                FundDetail fundDetail = fundDetailDao.queryByFundCode(shareModel.getFundCode());
                if (fundDetail != null && fundDetail.getCarryOverDateRemark()!=null){
                    shareModel.setClearDate(fundDetail.getCarryOverDateRemark());
                }else {
                    shareModel.setClearDate("");
                }

                //totalAccumIncome = totalAccumIncome.add(shareDetailTotalModel.getAccumIncome());
                totalWorthValue = totalWorthValue.add(shareModel.getWorthValue());
                totalTodayIncome = totalTodayIncome.add(shareModel.getTodayIncome());
                //totalAccumIncome = MoneyUtils.add(totalAccumIncome, shareModel.getTotalAccumIncome());
                shareModel.setCanDividend(true);
                if (shareModel.getAppFundType() == 1109 || shareModel.getAppFundType() == 900000){
                    shareModel.setCanDividend(false);
                    shareModel.setDividendMethodString("当前基金仅支持红利再投资");

                }else {
                    if (!combSharesQryModels.isEmpty()) {
                        for (CombSharesQryModel combSharesQryModel : combSharesQryModels) {
                            if (shareModel.getFundCode().equals(combSharesQryModel.getFundCode())) {
                                shareModel.setCanDividend(false);
                                shareModel.setDividendMethodString("您持有的组合中有该基金，根据组合交易规则，组合基金暂不支持修改分红方式");
                                break;
                            }
                        }
                    }
                }
            }

            //加上全赎的累计收益
//            for (IncomeModel incomeModel : incomeModelsNormal){
//                if (!fundCodes.contains(incomeModel.getFundCode())){
//                    logger.debug("编码：{}",incomeModel.getFundCode());
//                    totalAccumIncome = MoneyUtils.add(totalAccumIncome, incomeModel.getAccumIncome());
//                }
//            }

            for (ConfirmModel confirm : confirms){
                if (!confirm.getTradeAcco().startsWith("ZH") && !fundCodes.contains(confirm.getFundCode())){
                    if (!CalculateAssetsUtils.isT0Fund(lookups,confirm.getFundCode())){
                        if (confirm.getTAConfirmFlag().equals(GlobalConstant.TACONFIRM_FLAG_CONFIRMED)){
                            if (confirm.getFundBusinCode().equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_BUY) || confirm.getFundBusinCode().equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_FIX_BUY)){
                                totalAccumIncome = MoneyUtils.subtract(totalAccumIncome, confirm.getTradeConfirmBalance());
                            }
                            if (confirm.getFundBusinCode().equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_SELL)){
                                totalAccumIncome = MoneyUtils.add(totalAccumIncome, confirm.getTradeConfirmBalance());
                            }
                        }
                    }
                }
            }
            logger.debug("totalAccumIncome2:{}",totalAccumIncome);
            //将全赎部分的分红 加到累计收益中
            BigDecimal totalDividend = new BigDecimal(0);
            for(DividendModel dividendModel : dividends){
                //现金分红
                if ("1".equals(dividendModel.getAutoBuy())
                    // 没有持仓（全赎）
                    && !fundCodes.contains(dividendModel.getFundCode())
                    //不是 T0基金
                    && !CalculateAssetsUtils.isT0Fund(lookups,dividendModel.getFundCode())
                    // 分红日在今天之前
                    && dividendModel.getDividendDate().compareTo(DateUtils.formatDate4(new Date())) < 0){
                    totalDividend = MoneyUtils.add(totalDividend,dividendModel.getDividendBalance());
                }
            }
            totalAccumIncome = MoneyUtils.add(totalAccumIncome,totalDividend);
            logger.debug("totalAccumIncome3:{}",totalAccumIncome);

//            // 查出这个用户下所有的未确认的交易记录，传ClientId
//            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
//                user.getExternalClientId(), "", "", "", GlobalConstant.TACONFIRM_FLAG_UNCONFIRM, "", "", "", "", "",
//                "", "", "");
//
//            // 查询结果
//            list = new ArrayList<>();
//            HundsunUtils.ConvertToList(map, list, result);
//            if (!result.getSuccess()) {
//                return result;
//            }
//            // populate applys
//            ApplyListResult applyListResult = new ApplyListResult();
//            List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, applyListResult, null, null);
//            // 把T0基金过滤掉，因为T0基金有专门的页面来显示
//            filterT0(applys, filterT0);
            //总资产=确认的资产+有效在途的金额
            BigDecimal totalInTransit = new BigDecimal(0);
            Integer numInTransit = CalculateAssetsUtils.numOfInTransitWithoutPortfolio(applys, lookups, null);
            totalInTransit = CalculateAssetsUtils.sumOfInTransitWithoutPortfolio(applys, lookups, null);
            totalWorthValue = MoneyUtils.add(totalWorthValue, totalInTransit);

            //将多卡的持有份额合并   并累加份额 资产 等信息
            List<ShareModel> normalSharesNew = new ArrayList<>();
            List<String> fundCodesNew = new ArrayList<>();
            for (ShareModel shareModel : normalShares){
                if (fundCodesNew.contains(shareModel.getFundCode())){
                    for (ShareModel shareModelNew : normalSharesNew){
                        if (shareModel.getFundCode().equals(shareModelNew.getFundCode())){
                            shareModelNew.setAvailableDueShare(MoneyUtils.add(shareModelNew.getAvailableDueShare(),shareModel.getAvailableDueShare()));
                            shareModelNew.setCost(MoneyUtils.add(shareModelNew.getCost(),shareModel.getCost()));
                            shareModelNew.setCurrentShare(MoneyUtils.add(shareModelNew.getCurrentShare(),shareModel.getCurrentShare()));
                            shareModelNew.setEnableShares(MoneyUtils.add(shareModelNew.getEnableShares(),shareModel.getEnableShares()));
                            shareModelNew.setFrozenShare(MoneyUtils.add(shareModelNew.getFrozenShare(),shareModel.getFrozenShare()));
                            Integer sellIntransit = shareModelNew.getSellInTransit() + shareModel.getSellInTransit();
                            shareModelNew.setSellInTransit(sellIntransit);
                            shareModelNew.setTodayIncome(MoneyUtils.add(shareModelNew.getTodayIncome(),shareModel.getTodayIncome()));
                            shareModelNew.setTotalAccumIncome(MoneyUtils.add(shareModelNew.getTotalAccumIncome(),shareModel.getTotalAccumIncome()));
                            shareModelNew.setWorthValue(MoneyUtils.add(shareModelNew.getWorthValue(),shareModel.getWorthValue()));

                            shareModelNew.getShareDetails().add(shareModel);
                        }
                    }
                }else {
                    List<ShareModel> sharesInside = new ArrayList<>();
                    //因为 如果用原来的模型会导致无限循环  所以要新new一个
                    ShareModel shareModelCopy = new ShareModel();
                    BeanUtils.copyProperties(shareModel,shareModelCopy);
                    shareModelCopy.setShareDetails(sharesInside);
                    shareModelCopy.getShareDetails().add(shareModel);
                    normalSharesNew.add(shareModelCopy);
                    fundCodesNew.add(shareModel.getFundCode());
                }
            }

            List<ShareModel> normalSharesReal = new ArrayList<>();
            for (int i = 1; i <= normalSharesNew.size(); i++) {
                if (i >= beginNum && i < requestNum + beginNum) {
                    normalSharesReal.add(normalSharesNew.get(i - 1));
                }
            }
            result.setSuccess(true);
            //result.setShareDetailTotalModels(normalShares);
            result.setNetValueDate(netValueDate);
            result.setShares(normalSharesReal);
            result.setTotalInTransit(totalInTransit);
            result.setTotalNumInTransit(numInTransit);
            result.setTotalAccumIncome(totalAccumIncome);
            result.setTotalFundBalance(totalFundBalance);
            result.setTotalTodayIncome(totalTodayIncome);
            result.setTotalShareIncome(totalShareIncome);
            result.setTotalWorthValue(totalWorthValue);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryShares - Exception:", exception);
            throw new SystemDBException("queryShares Failure!", exception);
        }
    }

    /**
     * 单个 基金的资产详情
     * @param userId
     * @param filterT0
     * @param fundCode
     * @return
     * @throws BaseException
     */
    @Override
    public BaseResult queryFundShares(Long userId, boolean filterT0, String fundCode) throws BaseException {
        logger.info("queryShares - userId=" + userId);

        BaseResult result = new BaseResult();
        try {
            // 获取T+0基金
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
            // Step 1: 获取用户
            User user = userDao.getEntity(userId);

            List<UserBank> userBankList = userBankDao.getListByUserId(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            // 根据恒生银行编号获取银行名称
            List<Lookup> lookupsBank = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);




            // Step 2: 资产查询, 查询该用户下的所有份额
            Map<String, ShareModel> shareMap = new HashMap<>();
            List<ShareModel> normalShares = new ArrayList<>();
            //分红
            List<DividendModel> dividends = getDividend(user,userBankList);
            //收益
            //List<IncomeModel> incomeModels = getIncome(user,userBankList);
            List<Map<String, Object>> list = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    List<Map<String, Object>> listAll = new ArrayList<>();

                    for (UserBank userBank : userBankList) {
                        // 查询该用户下的所有非组合基金份额
                        Map<String, Object> map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, userBank.getExternalTradeAccount(), "1", "", fundCode, "",
                            user.getExternalClientId(), "", "", null);
                        list = new ArrayList<>();
                        HundsunUtils.ConvertToList(map, list, result);
                        if (!result.getSuccess()) {
                            return result;
                        }
                        listAll.addAll(list);
                    }
                    normalShares = ModelPopulateUtils.populateNormalShare(listAll, lookupsBank);
                }
            }
//
            //份额明细
            ShareDetailQryDto shareDetailQryDto = ShareDetailQryDto.builder()
                .qry_beginrownum("1")
                .begin_date(DateUtils.formatDate4(new Date()))
                .client_id(user.getExternalClientId())
                .end_date(DateUtils.formatDate4(new Date()))
                .reqry_recordsum_flag("1")
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .sort_direction("1")
                .fund_code(fundCode)
                .build();
            Map<String, Object> map = hsShareDetailQryService.query(shareDetailQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            //普通基金
            List<ShareDetailQryModel> shareDetailQryFundModels = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        shareDetailQryFundModels = ModelPopulateUtils.populateShareDetailQryFund(list, userBank);
                    }
                }
            }

            // 把T0基金过滤掉，因为T0基金有专门的页面来显示
            if (filterT0) {
                List<Lookup> T0Funds = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
                Iterator<ShareModel> iterator = normalShares.iterator();
                while (iterator.hasNext()) {
                    ShareModel model = iterator.next();
                    if (ModelPopulateUtils.isT0Fund(T0Funds, model.getFundCode())) {
                        iterator.remove();
                    }
                }
            }

            // Step 3: 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (ShareModel shareModel : normalShares) {
                fundCodeList.add(shareModel.getFundCode());
            }

            List<FundNetValueResult> fundNetValueList = null;
            try {
                Date t = maxDate();
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, DateUtils.addDay(new Date(), -1));
            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryShares - fundDetailService - Exception:", ex);
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                return result;
            }

            // 如果没有找到聚源的，则默认为空
            if (fundNetValueList == null) {
                fundNetValueList = new ArrayList<>();
            }

            // Step 4: 从恒生获取交易确认信息
            // 入参为交易账号的话，只查询该交易账号下的交易, 如果为客户编号的话，则查询该客户编号下的所有交易账号的交易
            Map<String, Object> confirmMap = tradeConfirmQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1",
                user.getExternalClientId(), "", "");

            // 查询结果
            List<Map<String, Object>> mapList = new ArrayList<>();
            HundsunUtils.ConvertToList(confirmMap, mapList, result);
            if (!result.getSuccess()) {
                return result;
            }


            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(mapList);



            /**
             * 交易记录
             */
            // 查出这个用户下所有的未确认的交易记录，传ClientId
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), "", "", "", GlobalConstant.TACONFIRM_FLAG_UNCONFIRM, "", "", "", "", "",
                "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // populate applys
            ApplyListResult applyListResult = new ApplyListResult();
            List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, applyListResult, null, null);
            // 把T0基金过滤掉，因为T0基金有专门的页面来显示
            filterT0(applys, filterT0);

            // Step 5: 根据数据计算
            // 总持仓收益
            BigDecimal totalAccumIncome = new BigDecimal(0);
            logger.debug("totalAccumIncome1:{}",totalAccumIncome);

            // 总本金
            BigDecimal totalFundBalance = new BigDecimal(0);
            // 总资产
            BigDecimal totalWorthValue = new BigDecimal(0);
            // 总当日收益
            BigDecimal totalTodayIncome = new BigDecimal(0);
            //总持仓收益
            BigDecimal totalShareIncome = new BigDecimal(0);
            // 保存上一工作日净值
            Map<String, BigDecimal> netValues = new HashedMap();
            // 净值日期
            Date netValueDate = DateUtils.addDay(new Date(), -1); //默认净值日期为前一天

            //基金编码集合
            List<String> fundCodes = new ArrayList<>();

            for (ShareModel shareModel : normalShares) {
                if (StringUtils.isNotBlank(fundCode)){
                    if(!fundCode.equals(shareModel.getFundCode())){
                        continue;
                    }
                }
                FundModel fundModel = this.queryFund(shareModel.getFundCode());
                shareModel.setFundStatus(fundModel.getFundStatus());
                fundCodes.add(shareModel.getFundCode());
                List<UserBank> userBanks = bankService.getBanks(user.getUserId(), shareModel.getBankAccount());
                UserBank userBankForBranch = new UserBank();
                if (userBanks != null && !userBanks.isEmpty()) {
                    userBankForBranch = userBanks.get(0);
                    shareModel.setBranchBank(userBankForBranch.getBranchbank() == null ? "" : userBankForBranch.getBranchbank());
                    shareModel.setBranchBankName(userBankForBranch.getBankBranchName() == null ? "" : userBankForBranch.getBankBranchName());
                }

                // 更新基金
                // 设置净值默认值
                shareModel.setNetValue(new BigDecimal(0));
                // 设置今日收益默认值
                shareModel.setTodayIncome(new BigDecimal(0));
                // 设置今日收益率默认值
                shareModel.setTodayIncomeRate(new BigDecimal(0));
                // 设置基金信息
                for (FundNetValueResult fundNetValue : fundNetValueList) {
                    // 聚源没有找到该只基金
                    if (fundNetValue == null || StringUtil.isEmpty(fundNetValue.getFundCode()) || fundNetValue.getFundTypeCode() == null) {
                        continue;
                    }
                    // 以聚源的数据为准
                    if (fundNetValue.getFundCode().equals(shareModel.getFundCode())) {
                        shareModel.setFundName(fundNetValue.getChiNameAbbr());
                        shareModel.setNetValue(fundNetValue.getUnitNV());
                        shareModel.setNetValueDate(fundNetValue.getTradingDay());
                        netValueDate = fundNetValue.getTradingDay();
                        // 设置前端显示的基金类型
                        shareModel.setAppFundType(fundNetValue.getFundTypeCode());
                        if (fundNetValue.getFundTypeCode() != null) {
                            /**
                             * Shangdu Lin - 20170419
                             * 非货币基金、货币基金：如果净值日期小于当前工作日，则不计算确认日期是当前工作日的基金份额
                             */
                            BigDecimal currentShare = shareModel.getCurrentShare();
                            //计算当日收益
                            BigDecimal oldShares = currentShare;
                            for (ShareDetailQryModel shareDetailQryModel : shareDetailQryFundModels) {
                                //logger.debug("aaaaa 确认日期：{}, 基金编码：{}， 可用份额：{}",shareDetailQryModel.getAffirmDate(),shareDetailQryModel.getFundCode(),shareDetailQryModel.getEnableShares());
                                if (shareModel.getFundCode().equals(shareDetailQryModel.getFundCode()) && shareDetailQryModel.getAffirmDate().equals(DateUtils.formatDate4(new Date())) && shareModel.getTradeAccount().equals(shareDetailQryModel.getTradeAccount())) {
                                    //logger.debug("货币基金  当前份额：{}，确认份额：{},基金Code:{}，基金名称",oldShares,shareDetailQryModel.getEnableShares(),shareModel.getFundCode(),shareModel.getFundName());
                                    oldShares = oldShares.subtract(shareDetailQryModel.getEnableShares());
                                }
                            }
                            if (fundNetValue.getFundTypeCode() == FundConst.FUND_TYPE_CODE_CURRENCY) {
                                // 日收益=当前份额/10000*万份之收益
                                // TODO:有申购时：日收益=当前份额/10000*万份之收益
                                // 有赎回时: 日收益=当前份额/10000*万份之收益
                                // 日收益
                                BigDecimal dailyProfit = fundNetValue.getDailyProfit();
                                if (dailyProfit == null) {
                                    dailyProfit = new BigDecimal(0);
                                    //logger.error("queryShares-getDailyProfit is null for fund code ={}", shareModel.getFundCode());
                                }
                                BigDecimal todayIncome = oldShares.divide(new BigDecimal(10000), 4)
                                    .multiply(dailyProfit);
                                //logger.debug("万分收益：{}，今日收益：{}，当前份额：{}，老份额：{}",dailyProfit,todayIncome,currentShare);
                                BigDecimal totalDividend = new BigDecimal(0);
                                //计算昨日收益的现金分红部分
                                /**
                                 * 昨日收益加上分红
                                 *
                                 */
                                for (DividendModel dividendModel : dividends) {
                                    if ("1".equals(dividendModel.getAutoBuy()) && shareModel.getFundCode().equals(dividendModel.getFundCode())&& dividendModel.getTradeAccount().equals(shareModel.getTradeAccount())) {
                                        if (dividendModel.getDividendDate().equals(DateUtils.formatDate4(DateUtils.addDay(new Date(), -1)))) {
                                            todayIncome = MoneyUtils.add(todayIncome, dividendModel.getDividendBalance());
                                        }
                                    }
                                }
                                todayIncome = todayIncome.setScale(2, BigDecimal.ROUND_HALF_UP);
                                shareModel.setTodayIncome(todayIncome);
                                // 日收益率
                                // 份额明细查询没有这个字段
                                if (shareModel.getWorthValue() != null) {
                                    if (shareModel.getWorthValue().compareTo(new BigDecimal(0)) > 0) {
                                        BigDecimal todayIncomeRate = todayIncome.divide(shareModel.getWorthValue(), 4);
                                        shareModel.setTodayIncomeRate(todayIncomeRate);
                                    } else {
                                        shareModel.setTodayIncomeRate(new BigDecimal(0));
                                    }
                                    // 万份之收益
                                    shareModel.setDailyProfit(dailyProfit);
                                }

                            } else {
                                // 日收益=当前基金份额*（当天净值- 前一个工作日净值）
                                // TODO:有申购时：日收益=当前基金份额*（当天净值- 前一个工作日净值）
                                // 有赎回时: 日收益=当前基金份额*（当天净值 - 前一个工作日净值）
                                // 日收益率
                                BigDecimal todayIncomeRate = fundNetValue.getNvDailyGrowthRate();
                                if (todayIncomeRate == null) {
                                    todayIncomeRate = new BigDecimal(0);
                                    logger.error("queryShares-getNvDailyGrowthRate is null for fund code ={}", shareModel.getFundCode());
                                }
                                shareModel.setTodayIncomeRate(todayIncomeRate);
                                // 日收益=当前基金份额*（当天净值- 前一个工作日净值）
                                BigDecimal lastUnitValue = new BigDecimal(0);
                                if (netValues.containsKey(shareModel.getFundCode())) {
                                    lastUnitValue = netValues.get(shareModel.getFundCode());
                                } else {
                                    lastUnitValue = getLastUnitValue(shareModel.getFundCode(), fundNetValue.getTradingDay());
                                    netValues.put(shareModel.getFundCode(), lastUnitValue);
                                }
                                BigDecimal margin = MoneyUtils.subtract(fundNetValue.getUnitNV(), lastUnitValue);
                                BigDecimal todayIncome = oldShares.multiply(margin);
                                //计算昨日收益的现金分红部分
                                /**
                                 * 昨日收益加上分红
                                 *
                                 */
                                for (DividendModel dividendModel : dividends) {
                                    if ("1".equals(dividendModel.getAutoBuy()) && shareModel.getFundCode().equals(dividendModel.getFundCode())&& dividendModel.getTradeAccount().equals(shareModel.getTradeAccount())) {
                                        if (dividendModel.getDividendDate().equals(DateUtils.formatDate4(DateUtils.addDay(new Date(), -1)))) {
                                            todayIncome = MoneyUtils.add(todayIncome, dividendModel.getDividendBalance());
                                        }
                                    }
                                }
                                todayIncome = todayIncome.setScale(2, BigDecimal.ROUND_HALF_UP);
                                shareModel.setTodayIncome(todayIncome);

                            }
                        }

                        break;
                    }
                }

                // 更新最新的资产值
                // 资产=当前份额*净值
                BigDecimal netValue = shareModel.getNetValue();
                if (netValue == null) {
                    netValue = new BigDecimal(0);
                }
                BigDecimal worthValue = shareModel.getCurrentShare().multiply(netValue);
                worthValue = MoneyUtils.add(worthValue, shareModel.getUnpaidIncome());
                shareModel.setWorthValue(worthValue);
                BigDecimal totalDividend = new BigDecimal(0);

                for (DividendModel dividendModel : dividends) {
                    if ("1".equals(dividendModel.getAutoBuy()) && shareModel.getFundCode().equals(dividendModel.getFundCode())&& dividendModel.getTradeAccount().equals(shareModel.getTradeAccount())) {
                        if (dividendModel.getDividendDate().compareTo(DateUtils.formatDate4(new Date())) < 0) {
                            totalDividend = MoneyUtils.add(totalDividend, dividendModel.getDividendBalance());
                        }
                        if (DateUtils.formatDate4(new Date()).compareTo(dividendModel.getEquityRegDate()) >= 0 && DateUtils.formatDate4(new Date()).compareTo(dividendModel.getDividendDate()) <= 0) {
                            shareModel.setDividends("该基金最近有分红配送");
                        }
                    }
                }
                if (shareModel.getDividends() == null) {
                    shareModel.setDividends("");
                }
//                //累计收益
//                if (incomeModels.size() != 0) {
//                    for (IncomeModel incomeModel : incomeModels) {
//                        if (incomeModel.getFundCode().equals(shareModel.getFundCode())&& incomeModel.getTradeAccount().equals(shareModel.getTradeAccount())) {
//                            shareModel.setTotalAccumIncome(incomeModel.getAccumIncome());
//                            //shareModel.setTotalAccumIncome(MoneyUtils.add(incomeModel.getAccumIncome(), totalDividend));
//                            break;
//                        }
//                    }
//                }

                //累计收益新算法   资产 加 赎回金额 减 申购金额 加上现金分红
                BigDecimal accumIncome = MoneyUtils.add(worthValue, totalDividend);
                for (ConfirmModel confirm :  confirms){
                    if (confirm.getFundCode().equals(shareModel.getFundCode()) && confirm.getTradeAcco().equals(shareModel.getTradeAccount())){
                        if (confirm.getTAConfirmFlag().equals(GlobalConstant.TACONFIRM_FLAG_CONFIRMED)){
                            if (confirm.getFundBusinCode().equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_BUY) || confirm.getFundBusinCode().equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_FIX_BUY)){
                                accumIncome = MoneyUtils.subtract(accumIncome, confirm.getTradeConfirmBalance());
                            }
                            if (confirm.getFundBusinCode().equals(GlobalConstant.FUND_BUSIN_CODE_CONFIRM_SELL)){
                                accumIncome = MoneyUtils.add(accumIncome, confirm.getTradeConfirmBalance());
                            }
                        }
                    }
                }

                shareModel.setTotalAccumIncome(accumIncome);
                totalAccumIncome = MoneyUtils.add(totalAccumIncome, accumIncome);
                logger.debug("totalAccumIncome1:{}",totalAccumIncome);
                //赎回在途笔数
                Integer sellInTransit = CalculateAssetsUtils.numSellOfInTransit(applys, lookups, false, shareModel.getFundCode(), shareModel.getTradeAccount());
                shareModel.setSellInTransit(sellInTransit);


//                // 计算持仓收益率
//                shareModel.setAccumIncomeRate(new BigDecimal(0));
//                BigDecimal fundWorthValue = shareModel.getWorthValue();
//                if (fundWorthValue != null && fundWorthValue.compareTo(new BigDecimal(0)) > 0) {
//                    // 持仓收益率
//                    BigDecimal accumIncomeRate = shareModel.getShareIncome().divide(fundWorthValue, 4);
//                    shareModel.setAccumIncomeRate(accumIncomeRate);
//                }

                // 获取基金类型
                NewsModel newsModel = getRiskLevel(shareModel.getFundCode());
                if (newsModel != null) {
                    shareModel.setFundType(newsModel.getFundType());
                    shareModel.setRiskLevel(newsModel.getRiskLevel());
                    shareModel.setRiskLevelString(newsModel.getRiskLevelString());
                }

                SecurityProduct product = fundDetailService.getSecurityProduct(shareModel.getFundCode());

                if (product == null) {
                    logger.error("Security Product is not exist");
                    result.setErrorCode(ErrorConstant.ERROR_FUND_NOT_EXIST);
                    result.setSuccess(false);
                    result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
                    return result;
                }

                // 七日年化率
                FundMMYieldPerformance perf = fundMMYieldPerformanceService.getFundMMYieldPerformance(product.getInnerCode());
                if (perf != null && perf.getLatestWeeklyYield() != null ){
                    shareModel.setLatestWeeklyYield(MoneyUtils.formatMoney(perf.getLatestWeeklyYield(), 2));// 最近7日折算年收益率（%）
                }else{
                    shareModel.setLatestWeeklyYield("");
                }

                FundDetail fundDetail = fundDetailDao.queryByFundCode(shareModel.getFundCode());
                if (fundDetail != null && fundDetail.getCarryOverDateRemark()!=null){
                    shareModel.setClearDate(fundDetail.getCarryOverDateRemark());
                }else {
                    shareModel.setClearDate("");
                }

                //totalAccumIncome = totalAccumIncome.add(shareDetailTotalModel.getAccumIncome());
                totalWorthValue = totalWorthValue.add(shareModel.getWorthValue());
                totalTodayIncome = totalTodayIncome.add(shareModel.getTodayIncome());
                //totalAccumIncome = MoneyUtils.add(totalAccumIncome, shareModel.getTotalAccumIncome());
                shareModel.setCanDividend(true);
                if (shareModel.getAppFundType() == 1109 || shareModel.getAppFundType() == 900000){
                    shareModel.setCanDividend(false);
                }else {
                    List<FundCombinationMapping> funds = fundCombinationMappingDao.selectAll();
                    if (!funds.isEmpty()){
                        for (FundCombinationMapping fund : funds){
                            if (shareModel.getFundCode().equals(fund.getFundCode())){
                                shareModel.setCanDividend(false);
                            }
                        }
                    }
                }
            }

            //加上全赎的累计收益
//            for (IncomeModel incomeModel : incomeModelsNormal){
//                if (!fundCodes.contains(incomeModel.getFundCode())){
//                    logger.debug("编码：{}",incomeModel.getFundCode());
//                    totalAccumIncome = MoneyUtils.add(totalAccumIncome, incomeModel.getAccumIncome());
//                }
//            }


            //将多卡的持有份额合并   并累加份额 资产 等信息
            List<ShareModel> normalSharesNew = new ArrayList<>();
            List<String> fundCodesNew = new ArrayList<>();
            for (ShareModel shareModel : normalShares){
                if (fundCodesNew.contains(shareModel.getFundCode())){
                    for (ShareModel shareModelNew : normalSharesNew){
                        if (shareModel.getFundCode().equals(shareModelNew.getFundCode())){
                            shareModelNew.setAvailableDueShare(MoneyUtils.add(shareModelNew.getAvailableDueShare(),shareModel.getAvailableDueShare()));
                            shareModelNew.setCurrentShare(MoneyUtils.add(shareModelNew.getCurrentShare(),shareModel.getCurrentShare()));
                            shareModelNew.setCost(MoneyUtils.add(shareModelNew.getCost(),shareModel.getCost()));
                            shareModelNew.setEnableShares(MoneyUtils.add(shareModelNew.getEnableShares(),shareModel.getEnableShares()));
                            shareModelNew.setFrozenShare(MoneyUtils.add(shareModelNew.getFrozenShare(),shareModel.getFrozenShare()));
                            Integer sellIntransit = shareModelNew.getSellInTransit() + shareModel.getSellInTransit();
                            shareModelNew.setSellInTransit(sellIntransit);
                            shareModelNew.setTodayIncome(MoneyUtils.add(shareModelNew.getTodayIncome(),shareModel.getTodayIncome()));
                            shareModelNew.setTotalAccumIncome(MoneyUtils.add(shareModelNew.getTotalAccumIncome(),shareModel.getTotalAccumIncome()));
                            shareModelNew.setWorthValue(MoneyUtils.add(shareModelNew.getWorthValue(),shareModel.getWorthValue()));

                            shareModelNew.getShareDetails().add(shareModel);
                        }
                    }
                }else {
                    if (StringUtils.isNotBlank(fundCode)){
                        if(!fundCode.equals(shareModel.getFundCode())){
                            continue;
                        }
                    }
                    List<ShareModel> sharesInside = new ArrayList<>();
                    //因为 如果用原来的模型会导致无限循环  所以要新new一个
                    ShareModel shareModelCopy = new ShareModel();
                    BeanUtils.copyProperties(shareModel,shareModelCopy);
                    shareModelCopy.setShareDetails(sharesInside);
                    shareModelCopy.getShareDetails().add(shareModel);
                    normalSharesNew.add(shareModelCopy);
                    fundCodesNew.add(shareModel.getFundCode());
                }
            }

            result.setSuccess(true);
            result.setResult(normalSharesNew);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryShares - Exception:", exception);
            throw new SystemDBException("queryShares Failure!", exception);
        }
    }

    @Override
    public Date maxDate() {
        Calendar t = Calendar.getInstance();
        int hour = t.get(Calendar.HOUR_OF_DAY);
        if (hour >= 15) {
            t.set(Calendar.HOUR_OF_DAY, 15);
            t.set(Calendar.MINUTE, 0);
            t.set(Calendar.SECOND, 0);
        }
        return new Date(t.getTimeInMillis());
    }

    @Override
    public void filterT0(List<ApplyModel> applys, boolean filterT0) {
        if (filterT0) {
            List<Lookup> T0Funds = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
            Iterator<ApplyModel> iterator = applys.iterator();
            while (iterator.hasNext()) {
                ApplyModel model = iterator.next();
                if (ModelPopulateUtils.isT0Fund(T0Funds, model.getFundCode())) {
                    iterator.remove();
                }
            }
        }
    }

    @Override
    public void filterOrganBao(List<ApplyModel> applys) {
        List<Lookup> OrganBao = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_ORGAN_BAO);
        Iterator<ApplyModel> iterator = applys.iterator();
        while (iterator.hasNext()) {
            ApplyModel model = iterator.next();
            if (ModelPopulateUtils.isT0Fund(OrganBao, model.getFundCode())) {
                iterator.remove();
            }
        }
    }

    @Override
    public void getOrganBao(List<ApplyModel> applys) {
        List<Lookup> OrganBao = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_ORGAN_BAO);
        Iterator<ApplyModel> iterator = applys.iterator();
        while (iterator.hasNext()) {
            ApplyModel model = iterator.next();
            if (!ModelPopulateUtils.isT0Fund(OrganBao, model.getFundCode())) {
                iterator.remove();
            }
        }
    }

    /**
     * 获取上一工作日的净值，getFundNetValue会找出比lastDate小的最新的净值
     * <p>
     * Shangdu Lin - 20170417
     *
     * @param fundCode
     * @param currentWorkingDate
     * @return
     */
    @Override
    public BigDecimal getLastUnitValue(String fundCode, Date currentWorkingDate) {
        // 获取前一工作日的净值
        Date lastDate = DateUtils.addDay(currentWorkingDate, -1);
        FundNetValueResult result = fundDetailService.getFundNetValue(fundCode, lastDate);
        if (!result.getSuccess()) {
            throw new BaseException(ErrorConstant.ERROR_GILDATA_WRONG, result.getErrorMessage(), GlobalConstant.ERROR_TYPE_APPLICATION);
        }

        return result.getUnitNV();
    }

    @Override
    public List<FundRate> queryFundRate(String fund_code, FundOperation operation) {
        fund_code = StringUtil.blankIfNull(fund_code);
        ArrayList<FundRate> result = new ArrayList<>();
        for (Map<String, Object> rate_map : rateQuery.query(fund_code, operation.getHsCode())) {
            FundRate rate = new FundRate();
            rate.setBusin_flag((String) rate_map.get("busin_flag"));
            rate.setCust_type((String) rate_map.get("cust_type"));
            rate.setFare_type((String) rate_map.get("fare_type"));
            rate.setFund_code((String) rate_map.get("fund_code"));
            rate.setMax_balance((String) rate_map.get("max_balance"));
            rate.setMax_fare((String) rate_map.get("max_fare"));
            rate.setMax_hold((String) rate_map.get("max_hold"));
            rate.setMax_predays((String) rate_map.get("max_predays"));
            rate.setMin_balance((String) rate_map.get("min_balance"));
            rate.setMin_fare((String) rate_map.get("min_fare"));
            rate.setMin_hold((String) rate_map.get("min_hold"));
            rate.setMin_predays((String) rate_map.get("min_predays"));
            rate.setRatio((String) rate_map.get("ratio"));
            rate.setSale_type((String) rate_map.get("sale_type"));
            rate.setShare_type((String) rate_map.get("share_type"));
            rate.setZoneno((String) rate_map.get("zoneno"));
            result.add(rate);
        }
        return result;
    }

    @Override
    public List<FundDiscountSetting> queryFundDiscountSettings(String fund_code, FundOperation operation) {
        fund_code = StringUtil.blankIfNull(fund_code);
        List<Map<String, Object>> res = discountSettingQuery.query(fund_code, operation.getHsCode());
        ArrayList<FundDiscountSetting> diss = new ArrayList<>(res.size());
        for (Map<String, Object> dis : res) {
            FundDiscountSetting setting = new FundDiscountSetting();
            setting.setSuccess_type((String) dis.get("success_type"));
            setting.setTrust_way((String) dis.get("trust_way"));
            setting.setFund_busin_code((String) dis.get("fund_busin_code"));
            setting.setCust_kind((String) dis.get("cust_kind"));
            setting.setCust_type((String) dis.get("cust_type"));
            setting.setFund_code((String) dis.get("fund_code"));
            setting.setShare_type((String) dis.get("share_type"));
            setting.setBelongdate_begin((String) dis.get("belongdate_begin"));
            setting.setBelongdate_end((String) dis.get("belongdate_end"));
            setting.setDiscount((String) dis.get("discount"));
            setting.setEnd_discount((String) dis.get("end_discount"));
            setting.setOther_discount((String) dis.get("other_discount"));
            setting.setMin_bala((String) dis.get("min_bala"));
            setting.setMax_bala((String) dis.get("max_bala"));
            setting.setMin_fare_ratio((String) dis.get("min_fare_ratio"));
            diss.add(setting);
        }
        return diss;
    }

    @Override
    public FundTradeRatio getApplyFundRate(String fund_code, BigDecimal balance, String custType) {

        //FIXME custType
        return this.calcFundOpeationFee(fund_code, balance, custType, FundOperation.Apply);
    }

    @Override
    public FundTradeRatio getExceedFundRate(String fund_code, BigDecimal balance, String custType) {
        //FIXME custType
        // 暂时赎回费率没有优惠
        return this.calcFundOpeationFee(fund_code, balance, custType, FundOperation.Exceed);
    }

    private FundTradeRatio calcFundOpeationFee(String fund_code, BigDecimal balance, String custType, FundOperation operation) {

        FundRate rate = BizUtils.getMappingFundRate(this.queryFundRate(fund_code, operation), balance, custType);
        FundTradeRatio res = new FundTradeRatio();
        if (rate == null) {
            logger.warn(
                "No related fund_rate record found according fund_code : " + fund_code + ", balance : " + balance);
            res.setSuccess(false);
            // alter by Shangdu - 20161115
            // 柜台可以不配这个费率，改成不报警 - alter by Shangdu - 20170606
            res.setErrorType(GlobalConstant.ERROR_TYPE_BUSINESS);
            res.setErrorMessage("未找到该基金当前金额对应的费率");
            res.setFund_code(fund_code);
            return res;
        }
        FundDiscountSetting discountSetting = BizUtils
            .getMappingFundDiscountSetting(this.queryFundDiscountSettings(fund_code, operation), balance);
        if (discountSetting == null) {
            logger.info("No discount setting for fund_code : " + fund_code + ", balance : " + balance);
        }
        res.setFund_code(fund_code);
        res.setBalance(MoneyUtils.formatMoney(balance, 2));
        res.setRatio(rate.getRatio());
        res.setMin_fare(rate.getMin_fare());
        res.setMax_fare(rate.getMax_fare());
        if (discountSetting != null) {
            res.setDiscount(discountSetting.getDiscount());
            res.setEnd_discount(discountSetting.getEnd_discount());
            res.setOther_discount(discountSetting.getOther_discount());
            res.setMin_fare_ratio(discountSetting.getMin_fare_ratio());
        }
        BigDecimal ratio = new BigDecimal(rate.getRatio());
        if (ratio.compareTo(BigDecimal.ZERO) == 0) {
            BigDecimal origin_fee = new BigDecimal(rate.getMin_fare());
            if (origin_fee.compareTo(BigDecimal.ZERO) == 0) {
                origin_fee = new BigDecimal(rate.getMax_fare());
            }
            String fee = MoneyUtils.formatMoney(origin_fee, 2);
            res.setOrigin_fee(fee);
            res.setFee(fee);
            res.setDiscount_fee(MoneyUtils.formatMoney(BigDecimal.ZERO, 2));
        } else {
            BigDecimal origin_fee = MoneyUtils.getFundApplyFee(balance, ratio);
            res.setOrigin_fee(MoneyUtils.formatMoney(origin_fee, 2));
            BigDecimal fee = MoneyUtils.getFundApplyFee(balance, BizUtils.getFundLastRatio(discountSetting, ratio));
            res.setFee(MoneyUtils.formatMoney(fee, 2));
            res.setDiscount_fee(MoneyUtils.formatMoney(origin_fee.subtract(fee), 2));
        }
        return res;
    }

    /**
     * 取得已定投的基金
     *
     * @param userId 用户Id
     * @return 定投基金列表
     */
    @Override
    public FixListResult getFixes(Long userId, Integer requestNum, Integer queryFlag, Integer beginNum,
                                  String sortDirection) throws BaseException {
        logger.info("getFixes - userId=" + userId);
        try {
            // 结果
            FixListResult result = new FixListResult();
            result.setSuccess(true);

            // 查询用户信息
            User user = userDao.getEntity(userId);

            // 查询已定投基金
            Map<String, Object> map = fixQuery.query(requestNum, queryFlag, beginNum, sortDirection,
                user.getExternalClientId());

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // 返回结果
            List<String> scheduledProtocolIdList = new ArrayList<String>();
            List<FixModel> fixes = populateFix(list, scheduledProtocolIdList, result);

            // 银行信息
            if (!scheduledProtocolIdList.isEmpty()) {
                setBankInfo(scheduledProtocolIdList, fixes);
            }

            result.setFixes(fixes);
            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "getFixes - Exception:", exception);
            throw new HundsunAPIException(ExceptionUtil.getMessage(exception));
        }

    }

    private void setBankInfo(List<String> scheduledProtocolIdList, List<FixModel> fixes) {
        List<FixBankModel> banks = tradeOrderDao.selectBankInfoByScheduledProtocolId(scheduledProtocolIdList);
        Map<String, FixBankModel> bankMap = new HashMap<String, FixBankModel>();
        for (FixBankModel bank : banks) {
            bankMap.put(bank.getScheduledProtocolId(), bank);
        }
        // 设置银行信息
        for (FixModel fix : fixes) {
            if (!bankMap.containsKey(fix.getScheduledProtocolId())) {
                continue;
            }
            FixBankModel bank = bankMap.get(fix.getScheduledProtocolId());
            fix.setBankAccount(bank.getBankAccount()); // 银行账户
            fix.setBankName(bank.getBankName()); // 银行名称
        }
    }

    /**
     * 将取到的Map数据组装成Model对象
     *
     * @param fixMapList
     * @return
     * @throws BaseException
     */
    private List<FixModel> populateFix(List<Map<String, Object>> fixMapList, List<String> scheduledProtocolIdList,
                                       FixListResult result) throws BaseException {

        // 定投基金列表
        List<FixModel> fixes = new ArrayList<FixModel>();

        // 基金代码列表
        List<String> fundCodes = new ArrayList<String>();

        for (Map<String, Object> item : fixMapList) {
            // 总记录数
            String totalCount = item.get(HSOutputParameterNames.TOTAL_COUNT).toString();
            if (StringUtil.isEmpty(totalCount)) {
                result.setTotalCount(0l);
            } else {
                result.setTotalCount(Long.parseLong(totalCount));
            }

            // 记录数
            String rowCount = item.get(HSOutputParameterNames.ROWCOUNT).toString();
            if (!StringUtil.isEmpty(rowCount)) {
                result.setRowCount(Long.parseLong(rowCount));
            } else {
                return fixes;
            }

            String fixState = item.get(HSOutputParameterNames.FIX_STATE).toString();// 定投状态
            // 状态是终止的，过滤掉
            /*
             * if (GlobalConstant.FIX_STATE_STOP.equals(fixState)) { continue; }
             */

            FixModel model = new FixModel();
            // 金额
            model.setBalance(BigDecimalUtil.ConvertToBigDecimal(item.get(HSOutputParameterNames.BALANCE).toString()));

            // 基金代码
            model.setFundCode(item.get(HSOutputParameterNames.FUND_CODE).toString());
            fundCodes.add(model.getFundCode());
            // 协议周期单位
            model.setProtocolPeriodUnit(item.get(HSOutputParameterNames.PROTOCOL_PERIOD_UNIT).toString());

            Long protocolFixDay = Long.parseLong(item.get(HSOutputParameterNames.PROTOCOL_FIX_DAY).toString());
            // 如果协议周期单位是周，需要减1（恒生那边周一的值是2开始的）
            if (GlobalConstant.PROTOCOL_PERIOD_UNIT_WEEK.equals(model.getProtocolPeriodUnit())) {
                protocolFixDay = protocolFixDay - 1;
            }
            // 定投交易日
            model.setProtocolFixDay(protocolFixDay);

            // 累计确认金额
            model.setTotalCfmBala(
                BigDecimalUtil.ConvertToBigDecimal(item.get(HSOutputParameterNames.TOTAL_CFM_BALA).toString()));
            // 定投协议号
            model.setScheduledProtocolId(item.get(HSOutputParameterNames.SCHEDULED_PROTOCOL_ID).toString());

            // 终止日期
            model.setExpiryDate(item.get(HSOutputParameterNames.EXPIRY_DATE).toString());

            // 协议状态
            model.setFixState(fixState);
            // 资金方式（支付方式）
            model.setCapitalMode(item.get(HSOutputParameterNames.CAPITAL_MODE).toString());

            // 下次扣款日期
            String nextFixDate = item.get(HSOutputParameterNames.NEXT_FIX_REQUEST_DATE).toString();
            // 将日期转换成YYYY-MM-DD
            model.setNextFixRequestDate(DateUtils.String8ToDateStr(nextFixDate));

            scheduledProtocolIdList.add(model.getScheduledProtocolId());
            fixes.add(model);
        }

        if (scheduledProtocolIdList.isEmpty()) {
            return fixes;
        }

        // 产品
        List<SecurityProduct> products = fundDetailService.getSecurityProducts(fundCodes);
        // 将列表转换为Map
        Map<String, String> codeMap = CalculateAssetsUtils.convertProductsToMap(products);

        // 根据code取得基金名称
        for (FixModel model : fixes) {
            if (codeMap.containsKey(model.getFundCode())) {
                // 设置名称
                model.setFundName(codeMap.get(model.getFundCode()));
            }
        }

        return fixes;
    }

    @Override
    public TAListResult queryTAAccounts(String clientId) throws BaseException {
        logger.info("queryTAAccounts - client Id:" + clientId);

        TAListResult result = new TAListResult();
        try {

            // 查询TA账户信息
            Map<String, Object> map = accountQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "", "", clientId,
                "", "", "");

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<AccountModel> accounts = ModelPopulateUtils.populateAccount(list, result);

            result.setSuccess(true);
            result.setAccounts(accounts);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryTAAccounts - Exception:", exception);
            throw new SystemDBException("queryTAAccounts Failure!", exception);
        }
    }

    /**
     * 获取用户的零钱包交易记录, 主要逻辑如下: 1, 检查用户是否开户 2, 获取请求数量的用户交易记录 3, 根据基金代码过滤,
     * 去掉非T0基金的交易记录, 去掉T0基金的非发生实际金额变动的零钱包交易记录(例如定投协议签约修改取消) 4,
     * 给返回的ApplyModel设置上fundType, 表明这是T0基金
     *
     * @author hzren
     * @since pangu003
     */
    @Override
    public ApplyListResult queryWalletOperationList(Long userId, Integer requestNum, Integer queryFlag,
                                                    Integer beginNum, String sortDirection, String fundCode) {
        Assert.notNull(userId);
        // 获取用户
        User user = userDao.getEntity(userId);
        ApplyListResult result = new ApplyListResult();
        // 判断是否已开户
        if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
            result.setErrorMessage(MessageUtil.getErrorMsg(result.getErrorCode()));
            return result;
        }
        // 查出这个用户下所有的交易记录，包括已确认和未确认的，传ClientId
        Map<String, Object> map = tradeApplyQueryService.query(requestNum, queryFlag, beginNum, sortDirection, "",
            user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "");
        // 检查查询结果是否OK
        List<Map<String, Object>> list = new ArrayList<>();
        HundsunUtils.ConvertToList(map, list, result);
        if (!result.getSuccess()) {
            return result;
        }
        // 银行名称
        List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
        //如果传了T0基金代码
        Set<String> temp = new HashSet<String>();
        temp.add(fundCode);
        final Set<String> t0fundCodeSet = StringUtils.isNotBlank(fundCode) ? temp : CollectionsUtils.newSet(
            lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND), new Function<Lookup, String>() {
                @Override
                public String apply(Lookup input) {
                    return input.getRuleValue();
                }
            });

        List<Lookup> businLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
        // populate
        List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, result, businLookups, bankLookups);
        // 过滤非T0基金, 并修改T0基金fundBusinCode 为T0申购代码
        org.apache.commons.collections.CollectionUtils.filter(applys, new Predicate() {

            @Override
            public boolean evaluate(Object object) {
                ApplyModel am = (ApplyModel) object;
                am.setIsOrg(user.getIsOrg());
                String fundCode = am.getFundCode();
                if (!t0fundCodeSet.contains(fundCode)) {
                    return false;
                }
                String businCode = am.getFundBusinCode();
                if (FundBusinCodeUtil.isBuy(businCode) || FundBusinCodeUtil.isSell(businCode)) {
                    return true;
                }
                return false;
            }
        });
        // 设置rowCount和totalCount
        result.setRowCount(Long.valueOf(applys.size()));
        result.setTotalCount(Long.valueOf(applys.size()));
        // 按日期倒叙排序
        Collections.sort(applys, new Comparator<ApplyModel>() {

            @Override
            public int compare(ApplyModel o1, ApplyModel o2) {
                String o1No = o1.getAllotNo(), o2No = o2.getAllotNo();
                if (o1No.length() < o2No.length()) {
                    return 1;
                } else if (o1No.length() > o2No.length()) {
                    return -1;
                }
                return 0 - o1No.compareTo(o2No);
            }
        });

        List<FundNetValueResult> fundNetValueList = fundDetailService
            .getFundNetValueByBatch(Lists.newArrayList(t0fundCodeSet), new Date());
        // 如果没有找到聚源数据，则默认为空数组
        if (fundNetValueList == null) {
            fundNetValueList = new ArrayList<>();
        }

        // 判断是否有在途的交易
        boolean inTransit = false;
        for (ApplyModel model : applys) {
            // 渲染模式为T0基金
            model.setFundType(FundApplyType.T0);
            // 未确认并且不能是无效扣款状态
            if (model.getTAConfirmFlag().equals(GlobalConstant.TACONFIRM_FLAG_UNCONFIRM)
                && !model.getDeductStatus().equals(GlobalConstant.DEDUCT_STATUS_INVALID)) {
                inTransit = true;
            }
            // 设置基金信息
            for (FundNetValueResult fundNetValue : fundNetValueList) {
                if (fundNetValue == null) {
                    continue;
                }
                if (fundNetValue.getFundCode().equals(model.getFundCode())) {
                    model.setFundName(fundNetValue.getChiNameAbbr());
                    break;
                }
            }
        }
        // 是否有在途的交易
        result.setInTransit(inTransit);
        result.setSuccess(true);
        result.setApplys(applys);
        return result;
    }

    /**
     * 查询警示内容
     *
     * @return
     */
    @Override
    public List<WarningModel> queryWarnings() {
        // Step 1: 查询警示内容
        WarningContentQueryDto dto = new WarningContentQueryDto();
        dto.setRequest_num(GlobalConstant.MAX_REQUEST_NUM);
        dto.setWarning_channel(GlobalConstant.WARNING_CHANNEL_APP);
        // 启用
        dto.setWarning_state("1");
        Map<String, Object> map = warningContentQueryService.query(dto);
        // 查询结果
        BaseListResult result = new BaseListResult();
        List<Map<String, Object>> list = new ArrayList<>();
        HundsunUtils.ConvertToList(map, list, result);
        if (!result.getSuccess()) {
            throw new BaseException(result.getErrorCode(), result.getErrorMessage());
        }

        return ModelPopulateUtils.populateWarnings(list, result);
    }

    @Override
    public AccountApplyModel queryAccountLatestApply(String clientId) {

        Map<String, Object> map = accountApplyQryService.query(clientId);


        List<Map<String, Object>> list = new ArrayList<>();

        BaseResult result = new BaseResult();
        HundsunUtils.ConvertToList(map, list, result);

        if (!result.getSuccess()) {
            throw new BaseException(result.getErrorCode(), result.getErrorMessage());
        }

        // 查询最近一次申请记录
        Map<String, Object> latestValueMap;
        if (list.size() == 1) {
            latestValueMap = list.get(0);
        } else {
            latestValueMap = list.get(0);
            String dateFormat = "yyyyMMddHHmmss";
            for (int i = 1, j = list.size(); i < j; i++) {
                Map<String, Object> temp = list.get(i);

                String originalDate1 = String.valueOf(latestValueMap.get("original_date"));
                String originalDate2 = String.valueOf(temp.get("original_date"));

                Date date1 = DateUtil.parse(originalDate1, dateFormat);
                Date date2 = DateUtil.parse(originalDate2, dateFormat);

                if (date1.getTime() <= date2.getTime()) {
                    latestValueMap = temp;
                }
            }
        }


        AccountApplyModel model = new AccountApplyModel();

        model.setCheck_flag(ObjectUtils.toString(latestValueMap.get("check_flag")));
        model.setReject_reason(ObjectUtils.toString(latestValueMap.get("reject_reason")));

        return model;
    }

    @Override
    public RecommendedFundModel queryRecommendedFund() {

        String key = CacheUtil.getKey(CacheKeyConst.RECOMMEND_FUND_MODEL);

        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();


        RecommendedFundModel recommendedFundModel;

        String resultStr = valueOperations.get(key);

        if (StringUtil.isEmpty(resultStr)) {
            recommendedFundModel = fundBasicJobService.refreshRecommendedFund();
        } else {
            recommendedFundModel = JSON.parseObject(resultStr, RecommendedFundModel.class);
        }

        return recommendedFundModel;
    }

    @Override
    @Transactional
    public BaseResult changePlace() {
        List<BankName> bankNames = bankNameDao.selectAll();
        List<String> failure = new ArrayList<>();

        for (BankName bankName : bankNames) {
            int k = 0;
            if (bankName.getVcProvincename() != null) {
                List<CityName> cityNames = cityNameDao.selectByProvince(bankName.getVcProvincename());
                if (bankName.getVcCityname() != null) {
                    for (CityName cityName : cityNames) {
                        if (bankName.getVcCityname().substring(bankName.getVcCityname().length() - 1).equals("县")) {
                            bankName.setVcCityname(bankName.getVcCityname().substring(0, bankName.getVcCityname().length() - 1));
                        }
                        if (cityName.getVcCountyname().contains(bankName.getVcCityname())) {
                            bankName.setVcCityname(cityName.getVcCityname());
                            bankNameDao.updateByPrimaryKeySelective(bankName);
                            k = 1;
                            break;
                        }
                    }
                }
            }
            if (k == 0) {
                if (!failure.contains(bankName.getVcCityname())) {
                    failure.add(bankName.getVcCityname());
                    logger.debug("没有替换的城市：{}", bankName.getVcCityname());
                }
            }
        }
        BaseResult result = new BaseResult();
        result.setResult(failure);
        return result;
    }

    public Map<String, List<IncomeModel>> getCombIncome(Map<String, List<CombSharesQryModel>> combTradeMap, User user) {
        // 收益查询  组合    按交易账号查
        Map<String, List<IncomeModel>> mapIncome = new HashMap<>();
        if (combTradeMap.keySet() != null) {
            for (String tradeAccount : combTradeMap.keySet()) {
                String key = CacheUtil.getKey(UrlConstants.INCOME_QRY, user.getExternalClientId());
                // from cache
                ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

                String incomeQry = valueOperations.get(key);

                List<IncomeModel> combIncomeModels = new ArrayList<>();
                if (StringUtil.isNotEmpty(incomeQry)) {
                    try {
                        combIncomeModels = JSONArray.parseArray(incomeQry, IncomeModel.class);
                    } catch (Exception e) {
                        logger.error("fail to parse array", e);
                    }
                }else{
                    HsIncomeQueryDto hsIncomeQueryDto = HsIncomeQueryDto.builder()
                        .begin_date(DateUtils.formatDate4(DateUtils.addDay(new Date(), -3)))
                        .end_date(DateUtils.formatDate4(DateUtils.addDay(new Date(), 0)))
                        .client_id(user.getExternalClientId())
                        .qry_beginrownum(1)
                        .reqry_recordsum_flag("1")
                        .request_num(GlobalConstant.MAX_REQUEST_NUM)
                        .sort_direction("1")
                        .trade_acco(tradeAccount)
                        .trade_mode("0")
                        .build();
                    Map<String, Object> map = incomeQueryService.query(hsIncomeQueryDto);
                    List<Map<String, Object>> list = new ArrayList<>();
                    BaseResult result = new BaseResult();
                    HundsunUtils.ConvertToList(map, list, result);
                    if (!result.getSuccess()) {
                        throw new BaseException(result.getErrorCode(), result.getErrorMessage());
                    }
                    for (Map<String, Object> map1 : list){
                        map1.put(HSOutputParameterNames.TRADE_ACCO, tradeAccount);
                    }
                    combIncomeModels = ModelPopulateUtils.populateIncome(list);
                }

                mapIncome.put(tradeAccount, combIncomeModels);
            }
        }
        return mapIncome;
    }

    private void getAccumIncome(AssetFundModel wallet, Map<String, BigDecimal> mapDividend, List<IncomeModel> incomeModels) {
        //算出累计收益后  总资产需要加上累计收益 所以要重新算一遍
        BigDecimal totalAccumIncome = new BigDecimal(0);
        List<String> fundCodes = new ArrayList<>();
        if (wallet.getFundModelList() != null) {
            List<AssetFundModel> aList = wallet.getFundModelList();
            //BigDecimal worthValue = null;
            //BigDecimal totalUnpaidIncome = null;
            for (AssetFundModel m : aList) {
                // 获得红利总金额
                BigDecimal totalDividends = mapDividend.get(m.getFundCode());
                // 累计收益 = 每月5号06文件中的“红利资金” + 每日05文件中的“货币基金未分配收益金额”
                BigDecimal temp = MoneyUtils.add(totalDividends, m.getUnpaidIncome());
                m.setTotalAccumIncome(temp);
                totalAccumIncome = MoneyUtils.add(totalAccumIncome, temp);
                //logger.warn("基金：{}，累计收益：{}，红利资金：{}，货币基金未分配收益金额：{}", m.getFundCode(), m.getTotalAccumIncome(), totalDividends, m.
                //getUnpaidIncome());
                fundCodes.add(m.getFundCode());
            }
        }
        for (IncomeModel incomeModel : incomeModels){
            if (!fundCodes.contains(incomeModel.getFundCode())){
                totalAccumIncome = MoneyUtils.add(totalAccumIncome, incomeModel.getAccumIncome());
            }
        }
        wallet.setTotalAccumIncome(totalAccumIncome);
    }

//    private void getAccumIncomeNew (AssetFundModel wallet, List<ApplyModel> applyModels){
//        BigDecimal totalAccumIncome = new BigDecimal(0);
//        if (wallet.getFundModelList() != null) {
//            List<AssetFundModel> aList = wallet.getFundModelList();
//            for (AssetFundModel m : aList) {
//                for (ApplyModel applyModel : applyModels){
//                    if(m.getFundCode().equals(applyModel))
//                }
//            }
//        }
//    }

    /**
     * 从redis中获取 普通基金分红
     * @param user
     * @param userBankList
     * @return
     */
    @Override
    public List<DividendModel> getDividend(User user, List<UserBank> userBankList){

        String key = CacheUtil.getKey(UrlConstants.DIVI_QRY, user.getExternalClientId());
        // from cache
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

        String diviQry = valueOperations.get(key);

        List<DividendModel> dividends = new ArrayList<>();
        if (StringUtil.isNotEmpty(diviQry)) {
            try {
                dividends = JSONArray.parseArray(diviQry, DividendModel.class);
            } catch (Exception e) {
                logger.error("fail to parse array", e);
            }
        } else{
            List<Map<String, Object>> listDividend = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {

                        Map<String, Object> map = dividendsQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", userBank.getExternalTradeAccount(), "", user.getExternalClientId(), "");
                        List<Map<String, Object>> list = new ArrayList<>();
                        AssetsResult result = new AssetsResult();
                        HundsunUtils.ConvertToList(map, list, result);

                        if (!result.getSuccess()) {
                            throw new BaseException(result.getErrorCode(), result.getErrorMessage());
                        }
                        listDividend.addAll(list);
                    }
                }
            }
            // populate
            BaseListResult baseListResult = new BankListResult();
            dividends = ModelPopulateUtils.populateDividend(listDividend, baseListResult);
            valueOperations.set(key, JSONArray.toJSONString(dividends), GlobalConstant.ASSETS_EXPIRED_HOURS_DEFAULT, TimeUnit.MINUTES);

        }
        return dividends;
    }


    /**
     * 从redis中获取 普通基金收益
     * @param user
     * @param userBankList
     * @return
     */
    private List<IncomeModel> getIncome(User user, List<UserBank> userBankList){
        String incomeKey = CacheUtil.getKey(UrlConstants.INCOME_QRY, user.getExternalClientId());

        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

        String incomeQry = valueOperations.get(incomeKey);

        List<IncomeModel> incomeModels = new ArrayList<>();
        if (StringUtil.isNotEmpty(incomeQry)) {
            try {
                incomeModels = JSONArray.parseArray(incomeQry, IncomeModel.class);
            } catch (Exception e) {
                logger.error("fail to parse array", e);
            }
        }else {
            List<Map<String, Object>> listIncome = new ArrayList<>();

            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        // Income接口
                        HsIncomeQueryDto hsIncomeQueryDto = HsIncomeQueryDto.builder()
                            .begin_date(DateUtils.formatDate4(DateUtils.addDay(new Date(), -10)))
                            .end_date(DateUtils.formatDate4(DateUtils.addDay(new Date(), 0)))
                            .client_id(user.getExternalClientId())
                            .qry_beginrownum(1)
                            .reqry_recordsum_flag("1")
                            .request_num(GlobalConstant.MAX_REQUEST_NUM)
                            .sort_direction("1")
                            .trade_acco(userBank.getExternalTradeAccount())
                            .trade_mode("0")
                            .build();
                        Map<String, Object> map = incomeQueryService.query(hsIncomeQueryDto);
                        List<Map<String, Object>> list = new ArrayList<>();
                        AssetsResult result = new AssetsResult();
                        HundsunUtils.ConvertToList(map, list, result);
                        if (!result.getSuccess()) {
                            throw new BaseException(result.getErrorCode(), result.getErrorMessage());
                        }
                        for (Map<String, Object> map1 : list){
                            map1.put(HSOutputParameterNames.TRADE_ACCO, userBank.getExternalTradeAccount());
                        }
                        listIncome.addAll(list);

                    }
                }
            }
            // populate
            incomeModels = ModelPopulateUtils.populateIncome(listIncome);
            valueOperations.set(incomeKey, JSONArray.toJSONString(incomeModels), GlobalConstant.ASSETS_EXPIRED_HOURS_DEFAULT, TimeUnit.MINUTES);
        }
        return incomeModels;
    }

    /**
     * 组合查询接口  redis
     */
    @Override
    public List<PortfolioQryModel> getPortfolioQryModel (User user){
        String portfolioKey = CacheUtil.getKey(UrlConstants.PORTFOLIO_QRY, user.getExternalClientId());

        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

        String portfolioQry = valueOperations.get(portfolioKey);

        List<PortfolioQryModel> portfolioQryModels = new ArrayList<>();
        if (StringUtil.isNotEmpty(portfolioQry)) {
            try {
                portfolioQryModels = JSONArray.parseArray(portfolioQry, PortfolioQryModel.class);
            } catch (Exception e) {
                logger.error("fail to parse array", e);
            }
        }else {
            //组合投资查询
            PortfolioQryDto portfolioQryDto = PortfolioQryDto.builder()
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .reqry_recordsum_flag("1")
                .sort_direction("1")
                .client_id(user.getExternalClientId())
                .qry_beginrownum("1")
                .build();

            Map<String, Object> map = portfolioQryService.query(portfolioQryDto);
            List<Map<String, Object>> list = new ArrayList<>();
            AssetsResult result = new AssetsResult();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                throw new BaseException(result.getErrorCode(), result.getErrorMessage());

            }
            //将同一组合的基金放在一起  存入一个map
            portfolioQryModels = ModelPopulateUtils.populatePortfolioQry(list);
            valueOperations.set(portfolioKey, JSONArray.toJSONString(portfolioQryModels), GlobalConstant.ASSETS_EXPIRED_HOURS_DEFAULT, TimeUnit.MINUTES);
        }
        return portfolioQryModels;
    }

    @Override
    public BaseResult checkSharesExistByBankAccount(Long userId, String bankAccount, String capitalMode) {
        User user = userDao.getEntity(userId);
        BaseResult result = new BaseResult();

        // 判断是否已开户
        if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
            result.setSuccess(false);
            result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            return result;
        }

        //判断是否只有1张银行卡
        List<UserBank> userBanks = bankService.getListByUserId(userId);

        if (userBanks != null) {
            if (userBanks.size() == 1) {
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_UNBIND_BANK_ONE_LEAST);
                result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_UNBIND_BANK_ONE_LEAST));

                return result;
            }

            int appRegisterCount = 0;

            for (UserBank bank : userBanks) {
                if (! "1".equals(bank.getCapitalMode())) {
                    appRegisterCount++;
                }
            }

            if (appRegisterCount <= 1) {
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_UNBIND_BANK_ONE_LEAST_APP);
                result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_UNBIND_BANK_ONE_LEAST_APP));

                return result;
            }
        }

        /**
         * 特殊 柜台需另做判断
         */
        if (GlobalConstant.CAPITAL_MODE_COUNTER.equals(capitalMode)) {
            UserBank counter = bankService.getByBankNo(userId, bankAccount, capitalMode);

            if (counter != null) {
                Map<String, Object> map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, counter.getExternalTradeAccount(), "1", "", "", "",
                    user.getExternalClientId(), "", "", null);

                // 查询结果
                List<Map<String, Object>> list = new ArrayList<>();
                HundsunUtils.ConvertToList(map, list, result);
                if (!result.getSuccess()) {
                    return result;
                }

                // populate
                ShareListResult shareListResult = new ShareListResult();
                List<ShareModel> shares = ModelPopulateUtils.populateShare(list, null, shareListResult);

                //如果用户有基金份额，并且跟需要解绑的银行账号一样，则返回不能解绑
                if (shares != null) {
                    for (ShareModel share : shares) {
                        String bankNo = share.getBankAccount().trim();

                        if (bankAccount.equals(bankNo)) {
                            result.setSuccess(false);
                            result.setErrorCode(ErrorConstant.ERROR_UNBUND_BANK_SHARE_EXISTS);
                            result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_UNBUND_BANK_SHARE_EXISTS));
                            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);

                            return result;
                        }
                    }
                } else {
                    result.setSuccess(true);

                    return result;
                }


            }
        }


        /**
         * Step 1: 资产查询 非柜台
         */
        // 查询该用户下的所有份额
        UserBank userBank = bankService.getByBankNo(userId, bankAccount, capitalMode);
        Map<String, Object> map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, userBank.getExternalTradeAccount(), "1", "", "", "",
            user.getExternalClientId(), "", "", null);

        // 查询结果
        List<Map<String, Object>> list = new ArrayList<>();
        HundsunUtils.ConvertToList(map, list, result);
        if (!result.getSuccess()) {
            return result;
        }

        // populate
        ShareListResult shareListResult = new ShareListResult();
        List<ShareModel> shares = ModelPopulateUtils.populateShare(list, null, shareListResult);

        //如果用户有基金份额，并且跟需要解绑的银行账号一样，则返回不能解绑
        if (shares != null) {
            for (ShareModel share : shares) {
                String bankNo = share.getBankAccount().trim();

                if (bankAccount.equals(bankNo)) {
                    result.setSuccess(false);
                    result.setErrorCode(ErrorConstant.ERROR_UNBUND_BANK_SHARE_EXISTS);
                    result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_UNBUND_BANK_SHARE_EXISTS));
                    result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);

                    return result;
                }
            }
        }



        //2. 用户名下没有份额，查询是否有在途金额
        Date beginDate = DateUtils.addDay(new Date(), GlobalConstant.FAST_REDEEM_INTERVAL_DAYS_AFTER_BEFORE * (-1));
        Date endDate = DateUtils.addDay(new Date(), GlobalConstant.FAST_REDEEM_INTERVAL_DAYS_AFTER_BEFORE);

        ApplyListResult applyListResult = queryApplys(userId, beginDate, endDate,
            "", GlobalConstant.TACONFIRM_FLAG_UNCONFIRM,
            true);
        if (!applyListResult.getSuccess()) {
            throw new BaseException(applyListResult.getErrorCode(), applyListResult.getErrorMessage());
        }

        List<ApplyModel> applyModels = applyListResult.getApplys();

        if (applyModels != null) {
            for (ApplyModel applyModel : applyModels) {
                String receiveAccount = applyModel.getReceivableAccount().trim();

                if (bankAccount.equals(receiveAccount)) {
                    result.setSuccess(false);
                    result.setErrorCode(ErrorConstant.ERROR_UNBUND_BANK_SHARE_EXISTS);
                    result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_UNBUND_BANK_SHARE_EXISTS));
                    result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);

                    return result;
                }
            }
        }

        //2.1 判断是否有线下汇款的

        Date date = workingDateService.getLastWorkingDay(new Date()).getLastWorkingDay();
        date.setHours(15);
        date.setMinutes(0);
        date.setSeconds(0);
        Integer offLine = tradeOrderDao.getOffLineByOneWeek(date, userId, bankAccount);

        if (offLine >= 1) {
            result.setSuccess(false);
            result.setErrorCode(ErrorConstant.ERROR_UNBUND_BANK_SHARE_EXISTS);
            result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_UNBUND_BANK_SHARE_EXISTS));
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);

            return result;
        }



        //判断用户是否有定投协议
        FixListResult fixListResult = getFixes(userId, GlobalConstant.MAX_REQUEST_NUM, 1, 0, "1");

        if (fixListResult != null) {
            List<FixModel> fixModels = fixListResult.getFixes();

            if (ListUtil.isNotEmpty(fixModels)) {
                for (FixModel model : fixModels) {
                    if ("A".equals(model.getFixState()) && bankAccount.equals(model.getBankAccount())) {
                        result.setSuccess(false);
                        result.setErrorCode(ErrorConstant.ERROR_UNBUND_BANK_SHARE_EXISTS);
                        result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_UNBUND_BANK_SHARE_EXISTS));

                        return result;
                    }
                }
            }
        }




        return result;
    }

    /**
     * 获取当前工作日
     * @return
     */
    private Date getCurrencyDate(){
        String workingDayKey = CacheUtil.getKey(GlobalConstant.WORKING_DAY);

        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

        String workingDay = valueOperations.get(workingDayKey);

        Date currentWorkingDate = new Date();
        if (StringUtil.isNotEmpty(workingDay)) {
            try {
                currentWorkingDate = JSONArray.parseObject(workingDay, Date.class);
            } catch (Exception e) {
                logger.error("fail to parse array", e);
            }
        }else {
            currentWorkingDate = workingDateService.getWorkingDay(new Date());
        }
        return currentWorkingDate;
    }

    @Override
    public AssetsResult queryAccumlateIncomeForProfit(Long userId) throws BaseException {
        try {

            /**
             * Step 0: 验证数据
             */
            // 结果
            AssetsResult result = new AssetsResult();
            // 获取用户
            User user = userDao.getEntity(userId);
            // 获取 交易账号
            List<UserBank> userBankList = userBankDao.getListByUserId(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            // 获取T+0基金
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
            /**
             * Step 0-1:查询当前工作日
             */
            // 查询当前工作日
            Date currentWorkingDate = getCurrencyDate();
            // 根据恒生银行编号获取银行名称
            List<Lookup> banklookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            /**
             * Step 1: 资产查询
             */
            // 查询该用户下的所有份额
            HsShareQueryDto hsShareQueryDto = HsShareQueryDto.builder()
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .reqry_recordsum_flag("1")
                .qry_beginrownum(1)
                .sort_direction("1")
                .client_id(user.getExternalClientId())
                .build();
            Map<String, Object> map = shareQueryService.query(hsShareQueryDto);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            ShareListResult shareListResult = new ShareListResult();
            List<ShareModel> shares = ModelPopulateUtils.populateShare(list, null, shareListResult);


            /**
             * 普通基金份额查询  非组合部分
             */
//            Map<String, ShareModel> shareMap = new HashMap<>();
            List<ShareModel> normalShares = null;
            List<Map<String, Object>> listAll = new ArrayList<>();
            if (userBankList != null) {
                if (userBankList.size() != 0) {
                    for (UserBank userBank : userBankList) {
                        // 查询该用户下的所有非组合基金份额
                        map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, userBank.getExternalTradeAccount(), "1", "", "", "",
                            user.getExternalClientId(), "", "", null);
                        logger.warn("map：{}", map.get(GlobalConstant.KEY_LIST_MAP));

                        list = new ArrayList<>();
                        HundsunUtils.ConvertToList(map, list, result);
                        if (!result.getSuccess()) {
                            return result;
                        }
                        listAll.addAll(list);



                    }
                }
            }
            normalShares = ModelPopulateUtils.populateNormalShare(listAll, banklookups);
            //恒生 按交易账号查询 分红
            List<DividendModel> dividends = getDividend(user,userBankList);
            //恒生 按交易账号查询 收益
            //List<IncomeModel> incomeModels = getIncome(user,userBankList);

            /**
             *  组合份额查询
             */
            //CombSharesQryDto combSharesQryDto = hsCombSharesQryService.getDto("", "", "", "", GlobalConstant.MAX_REQUEST_NUM.toString(), "1", "", user.getExternalClientId(), "1", "", "", "", "");
            CombSharesQryDto combSharesQryDto = CombSharesQryDto.builder()
                .request_num(GlobalConstant.MAX_REQUEST_NUM)
                .reqry_recordsum_flag("1")
                .client_id(user.getExternalClientId())
                .qry_beginrownum("1")
                .build();
            map = hsCombSharesQryService.query(combSharesQryDto);
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            List<CombSharesQryModel> combSharesQryModels = ModelPopulateUtils.populateCombSharesQry(list);
            //将同一组合的基金放在一起  存入一个map
            Map<String, List<CombSharesQryModel>> combMap = new HashMap<>();
            List<String> combCode = new ArrayList<>();
            Map<String, List<CombSharesQryModel>> combTradeMap = new HashMap<>();
            //按交易账号区分
            portfolioService.getCombTradeMap(combSharesQryModels, combTradeMap);
            portfolioService.getCombMap(combSharesQryModels, combMap, combCode);

            //恒生 查组合
            List<PortfolioQryModel> portfolioQryModels = getPortfolioQryModel(user);
            Map<String, List<PortfolioQryModel>> portfolioMap = new HashMap<>();
            List<String> portfolioCode = new ArrayList<>();
            //PortfolioQryModelByCode portfolioQryModelByCode = new PortfolioQryModelByCode();
            portfolioService.getPortfolioMap(portfolioQryModels, portfolioMap, portfolioCode);
            //Map<String, List<PortfolioQryModel>> portfolioMap = new HashMap<>();

            // 收益查询  组合    按交易账号查
            //Map<String, List<IncomeModel>> mapIncome = getCombIncome(combTradeMap, user);

            /**
             * Step 2: 交易记录查询
             */
            // 查出这个用户下所有的未确认的交易记录，传ClientId
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", GlobalConstant.TACONFIRM_FLAG_UNCONFIRM, "", "", "", "", "", "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // populate applys
            ApplyListResult applyListResult = new ApplyListResult();
            List<ApplyModel> applys = ModelPopulateUtils.populateApply(list, applyListResult, null, null);
            List<String> applyPortfolioCode = new ArrayList<>();
            for (ApplyModel applyModel : applys) {
                if (!Strings.isNullOrEmpty(applyModel.getPortfolioCode()) && !applyPortfolioCode.contains(applyModel.getPortfolioCode())) {
                    applyPortfolioCode.add(applyModel.getPortfolioCode());
                }
            }

            //交易申请查询  用于计算瑞富宝累计收益
            list = new ArrayList<>();
            map = tradeApplyQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "",
                user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "");


            // 查询结果
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }
            // 交易业务名称
            List<Lookup> tradeLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_BUSINAME);
            // 银行名称
            List<Lookup> bankLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            // populate
            List<ApplyModel> applyModels = ModelPopulateUtils.populateApply2(list, applyListResult, tradeLookups, bankLookups, user.getIsOrg());


            //交易确认  用于计算累计收益
            map = tradeConfirmQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "1", "", user.getExternalClientId(), "", "", "", "", "", "", "", "", "", "", "", "", "", "");

            // 查询结果
            list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);
            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ConfirmModel> confirms = ModelPopulateUtils.populateConfirm(list);


            /**
             * Step 3: 从聚源获取基金信息
             */

            // 从聚源获取基金信息
            List<String> fundCodeList = new ArrayList<String>();
            for (ShareModel shareModel : shares) {
                if (shareModel.getFundCode().equals("161728")) {
                    continue;
                }
                if (!fundCodeList.contains(shareModel.getFundCode())){

                    fundCodeList.add(shareModel.getFundCode());
                }
            }

            List<FundNetValueResult> fundNetValueList = null;
            boolean gliDataException = false;
            try {
                fundNetValueList = fundDetailService.getFundNetValueByBatch(fundCodeList, DateUtils.addDay(new Date(), -1));
            } catch (Exception ex) {
                ExceptionUtil.logError(logger, "queryAssets - fundDetailService - Exception:", ex);
                gliDataException = true;
            }

            // 如果没有找到聚源的，返回错误
            if (gliDataException || fundNetValueList == null) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_SYSTEM);
                result.setErrorCode(ErrorConstant.ERROR_GILDATA_WRONG);
                return result;
            }

            // 获取上一工作日净值
            Map<String, BigDecimal> lastNetValues = new HashedMap();
            for (FundNetValueResult netValueResult :
                fundNetValueList) {
                if (netValueResult != null) {
                    if (!lastNetValues.containsKey(netValueResult.getFundCode())) {
                        BigDecimal lastUnitValue = getLastUnitValue(netValueResult.getFundCode(), netValueResult.getTradingDay());
                        lastNetValues.put(netValueResult.getFundCode(), lastUnitValue);
                    }
                }
            }


            /**
             * Step 4: 计算出前后15天未确认的快速赎回的份额（如果要精确计算的话，只要算当前工作日以及上一工作日的未T+
             * 1确认的实时成功的快速赎回的份额）
             */

            Date beginDate = DateUtils.addDay(new Date(), GlobalConstant.FAST_REDEEM_INTERVAL_DAYS_AFTER_BEFORE * (-1));
            Date endDate = DateUtils.addDay(new Date(), GlobalConstant.FAST_REDEEM_INTERVAL_DAYS_AFTER_BEFORE);

            applyListResult = queryApplys(userId, beginDate, endDate,
                GlobalConstant.FUND_BUSIN_CODE_T0_FAST_APPLY_SELL, GlobalConstant.TACONFIRM_FLAG_REAL_SUCCESS,
                true);
            if (!applyListResult.getSuccess()) {
                throw new BaseException(applyListResult.getErrorCode(), applyListResult.getErrorMessage());
            }

            //近期快速赎回的份额
            Map<String, BigDecimal> fastRedeemShares = CalculateAssetsUtils.getFastSellShares(applyListResult.getApplys());


            /**
             * Step 5: 计算结果值
             */
            //计算组合份额，资产 日收益
            List<PortfolioModel> portfolioModels = new ArrayList<>();

            //按交易账号 区分开的分红列表
            Map<String, List<DividendModel>> dividendMap = new HashMap<>();
            for (String key : combTradeMap.keySet()) {
                getDividends(key, user, dividendMap);
            }

            for (String code : combCode) {
                if (combMap.get(code) == null || combMap.get(code).isEmpty()) {
                    continue;
                }
                PortfolioModel portfolioModel = assetsQueryService.extractPortfolioForProfit(combMap.get(code), fundNetValueList, dividendMap, confirms,userId,user.getExternalClientId());

                portfolioModels.add(portfolioModel);
            }


            List<String> portfolioTradeAccounts = new ArrayList<>();


            for (PortfolioModel portfolioModel : portfolioModels) {
                portfolioTradeAccounts.add(portfolioModel.getTradeAcco());

            }

            assetsQueryService.getPortfolioAccumIncomeForProfit(confirms,portfolioTradeAccounts,dividendMap,userId,user.getExternalClientId());


            // 计算份额，净值日期， 日收益率，万份收益
            Set<String> fundCodes = new HashSet<>();
            List<AssetFundModel> funds = assetsQueryService.extractFunds(shares, lookups, fundNetValueList,fundCodes);
            // 计算总和值，是否有在途
            // 汇总列表
            List<AssetFundModel> sumOfFunds = new ArrayList<>();
            // 零钱包 - 计算零钱包总资产和零钱包总收益
            // 零钱包资产要减去当前工作日已快速赎回的部分，因为快速赎回的部分当前工作日已经没有日收益
            AssetFundModel wallet = assetsQueryService.sumOfAsset(funds, lookups, true,
                null, dividends,confirms, applyModels,userId,user.getExternalClientId(),fundCodes);
            sumOfFunds.add(wallet);





            /**
             * 非组合基金的计算
             */


            List<AssetFundModel> normalFunds = CalculateAssetsUtils.extractFunds(normalShares, lookups, fundNetValueList);
            AssetFundModel normalFund = assetsQueryService.sumOfAsset(normalFunds, lookups, false, fastRedeemShares,
                 dividends,confirms,applyModels,userId,user.getExternalClientId(),fundCodes);


            // 返回结果
            result.setSuccess(true);

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryAssets - Exception:", exception);
            throw new SystemDBException("queryAssets Failure!", exception);
        }
    }


    @Override
    public void setClient() {
        List<TradeOrders> tradeOrders = tradeOrdersDao.selectNoClientId();
        for (TradeOrders tradeOrders1 : tradeOrders) {
            User user = userDao.getEntity(tradeOrders1.getUserId().longValue());
            tradeOrders1.setClientId(user.getExternalClientId());
            tradeOrdersDao.updateByPrimaryKeySelective(tradeOrders1);
        }
    }
}
